diff --git a/build.gradle b/build.gradle index e3fb4f8..2e82a4b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'fabric-loom' version '1.0-SNAPSHOT' - id 'io.github.juuxel.loom-quiltflower' version '1.7.4' + id 'fabric-loom' version '1.1.9' + id 'io.github.juuxel.loom-quiltflower' version '1.8.0' id 'maven-publish' } diff --git a/gradle.properties b/gradle.properties index 72d704f..12ad567 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ loader_version=0.14.12 fabric_version=0.72.0+1.19.2 # Mod Properties -mod_version=1.5.4+1.19.2 +mod_version=1.5.5+1.19.2 maven_group=jerozgen archives_base_name=language-reload diff --git a/src/main/java/jerozgen/languagereload/LanguageReload.java b/src/main/java/jerozgen/languagereload/LanguageReload.java index e79e425..4925114 100644 --- a/src/main/java/jerozgen/languagereload/LanguageReload.java +++ b/src/main/java/jerozgen/languagereload/LanguageReload.java @@ -9,7 +9,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; import net.minecraft.client.gui.screen.ingame.BookScreen; -import net.minecraft.client.resource.language.LanguageDefinition; +import net.minecraft.client.resource.language.LanguageManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -59,23 +59,26 @@ public static void reloadSearch() { ((MinecraftClientAccessor) client).getSearchManager().reload(client.getResourceManager()); } - public static void setLanguage(LanguageDefinition language, LinkedList fallbacks) { + public static void setLanguage(String language, LinkedList fallbacks) { var client = MinecraftClient.getInstance(); var languageManager = client.getLanguageManager(); var config = Config.getInstance(); - var languageIsSame = languageManager.getLanguage().equals(language); + var languageIsSame = languageManager.getLanguage().getCode().equals(language); var fallbacksAreSame = config.fallbacks.equals(fallbacks); if (languageIsSame && fallbacksAreSame) return; + if (languageManager.getLanguage(language) == null) + language = LanguageManager.DEFAULT_LANGUAGE_CODE; + config.previousLanguage = languageManager.getLanguage().getCode(); config.previousFallbacks = config.fallbacks; - config.language = language.getCode(); + config.language = language; config.fallbacks = fallbacks; Config.save(); - languageManager.setLanguage(language); - client.options.language = language.getCode(); + languageManager.setLanguage(languageManager.getLanguage(language)); + client.options.language = language; client.options.write(); reloadLanguages(); diff --git a/src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java b/src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java new file mode 100644 index 0000000..3116f08 --- /dev/null +++ b/src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java @@ -0,0 +1,7 @@ +package jerozgen.languagereload.access; + +import jerozgen.languagereload.gui.LanguageEntry; + +public interface ILanguageOptionsScreen { + void focusEntry(LanguageEntry entry); +} diff --git a/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java b/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java index e2db769..5758916 100644 --- a/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java +++ b/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java @@ -3,94 +3,111 @@ import jerozgen.languagereload.LanguageReload; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawableHelper; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.client.gui.widget.ElementListWidget; +import net.minecraft.client.gui.widget.TexturedButtonWidget; import net.minecraft.client.resource.language.LanguageDefinition; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; +import java.util.*; -public abstract class LanguageEntry extends ElementListWidget.Entry { +public abstract class LanguageEntry extends AlwaysSelectedEntryListWidget.Entry { protected static final Identifier TEXTURE = new Identifier(LanguageReload.MOD_ID, "textures/gui/language_selection.png"); protected static final int TEXTURE_WIDTH = 64; protected static final int TEXTURE_HEIGHT = 64; protected static final int HOVERED_V_OFFSET = 24; - private final static int SCROLLBAR_WIDTH = 6; - private final static int LEFT_MARGIN = 2; - private final static int ENTRY_HEIGHT = 24; - protected final MinecraftClient client = MinecraftClient.getInstance(); + + protected final String code; protected final LanguageDefinition language; - protected final LinkedList selectedLanguages; + protected final LinkedList selectedLanguages; protected final Runnable refreshListsAction; private final List buttons = new ArrayList<>(); - public LanguageEntry(Runnable refreshListsAction, LanguageDefinition language, LinkedList selectedLanguages) { + protected LanguageListWidget parentList; + + public LanguageEntry(Runnable refreshListsAction, String code, LanguageDefinition language, LinkedList selectedLanguages) { + this.code = code; this.language = language; this.selectedLanguages = selectedLanguages; this.refreshListsAction = refreshListsAction; } - protected ButtonWidget addChild(ButtonWidget button) { + protected ButtonWidget addButton(int width, int height, int u, int v, ButtonWidget.PressAction action) { + return addButton(new TexturedButtonWidget(0, 0, width, height, u, v, HOVERED_V_OFFSET, TEXTURE, TEXTURE_WIDTH, TEXTURE_HEIGHT, action)); + } + + protected ButtonWidget addButton(TexturedButtonWidget button) { button.visible = false; buttons.add(button); return button; } - protected abstract void renderButtons(ButtonRenderer buttonRenderer, int top, int left); - - public LanguageDefinition getLanguage() { - return language; + public boolean isFocused() { + return parentList.getSelectedOrNull() == this && parentList.isFocused(); } + public void toggle() {} + + public void moveUp() {} + + public void moveDown() {} + + protected abstract void renderButtons(ButtonRenderer buttonRenderer, int x, int y); + @Override - public void render(MatrixStack matrices, int index, int top, int left, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - if (client.options.getTouchscreen().getValue() || hovered) { - if (hovered) { - var right = left + entryWidth - SCROLLBAR_WIDTH - LEFT_MARGIN; - DrawableHelper.fill(matrices, left, top, right, top + ENTRY_HEIGHT, 0xA0909090); - } + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + x -= 2; + y -= 2; + if (hovered || isFocused() || client.options.getTouchscreen().getValue()) { + DrawableHelper.fill(matrices, + x + 1, y + 1, x + entryWidth - 1, y + entryHeight + 3, + (hovered || isFocused()) ? 0xA0909090 : 0x50909090); buttons.forEach(button -> button.visible = false); - ButtonRenderer buttonRenderer = (button, x, y) -> { - button.x = x; - button.y = y; + renderButtons((button, buttonX, buttonY) -> { + button.x = buttonX; + button.y = buttonY; button.visible = true; button.render(matrices, mouseX, mouseY, tickDelta); - }; - renderButtons(buttonRenderer, top, left); + }, x, y); } - client.textRenderer.drawWithShadow(matrices, language.getName(), left + 29, top + 3, 0xFFFFFF); - client.textRenderer.drawWithShadow(matrices, language.getRegion(), left + 29, top + 14, 0x808080); + client.textRenderer.drawWithShadow(matrices, language.getName(), x + 29, y + 3, 0xFFFFFF); + client.textRenderer.drawWithShadow(matrices, language.getRegion(), x + 29, y + 14, 0x808080); } - public void appendNarrations(NarrationMessageBuilder builder) { - builder.put(NarrationPart.TITLE, Text.translatable("narrator.select", language)); + @Override + public Text getNarration() { + return Text.translatable("narrator.select", language); } @Override - public boolean changeFocus(boolean lookForwards) { + public boolean mouseClicked(double mouseX, double mouseY, int button) { + for (var widget : buttons) + if (widget.mouseClicked(mouseX, mouseY, button)) { + return true; + } return false; } - @Override - public List children() { - return buttons; + public void setParent(LanguageListWidget list) { + this.parentList = list; } - @Override - public List selectableChildren() { - return buttons; + public LanguageListWidget getParent() { + return parentList; + } + + public String getCode() { + return code; + } + + public LanguageDefinition getLanguage() { + return language; } @FunctionalInterface diff --git a/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java b/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java index 98729c6..9364425 100644 --- a/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java +++ b/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java @@ -1,22 +1,32 @@ package jerozgen.languagereload.gui; +import jerozgen.languagereload.access.ILanguageOptionsScreen; +import jerozgen.languagereload.mixin.EntryListWidgetAccessor; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ElementListWidget; +import net.minecraft.client.gui.ParentElement; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.option.LanguageOptionsScreen; +import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.client.render.Tessellator; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; +import org.jetbrains.annotations.Nullable; -public class LanguageListWidget extends ElementListWidget { +import static org.lwjgl.glfw.GLFW.*; + +public class LanguageListWidget extends AlwaysSelectedEntryListWidget { private final Text title; + private final LanguageOptionsScreen screen; - public LanguageListWidget(MinecraftClient client, int width, int height, Text title) { + public LanguageListWidget(MinecraftClient client, LanguageOptionsScreen screen, int width, int height, Text title) { super(client, width, height, 48, height - 55 + 4, 24); this.title = title; + this.screen = screen; - setRenderHeader(true, (int) (9.0f * 1.5f)); + setRenderHeader(true, (int) (9f * 1.5f)); centerListVertically = false; } @@ -29,8 +39,67 @@ protected void renderHeader(MatrixStack matrices, int x, int y, Tessellator tess } @Override - public int getRowTop(int index) { - return super.getRowTop(index); + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + var selectedEntry = this.getSelectedOrNull(); + if (selectedEntry == null) return super.keyPressed(keyCode, scanCode, modifiers); + + if (keyCode == GLFW_KEY_SPACE || keyCode == GLFW_KEY_ENTER) { + selectedEntry.toggle(); + this.setFocused(null); + ((ILanguageOptionsScreen) screen).focusEntry(selectedEntry); + return true; + } + + if (Screen.hasShiftDown()) { + if (keyCode == GLFW_KEY_DOWN) { + selectedEntry.moveDown(); + return true; + } + if (keyCode == GLFW_KEY_UP) { + selectedEntry.moveUp(); + return true; + } + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + // Remove focusing on entry click + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + this.updateScrollingState(mouseX, mouseY, button); + if (!this.isMouseOver(mouseX, mouseY)) return false; + + var entry = this.getEntryAtPosition(mouseX, mouseY); + if (entry == null && button == 0) return true; + + if (entry != null && entry.mouseClicked(mouseX, mouseY, button)) { + var focusedEntry = this.getFocused(); + if (focusedEntry != entry && focusedEntry instanceof ParentElement parentElement) + parentElement.setFocused(null); + this.setDragging(true); + return true; + } + + return ((EntryListWidgetAccessor) this).isScrolling(); + } + + @Override + @Nullable + protected LanguageEntry getEntryAtPosition(double x, double y) { + int halfRowWidth = this.getRowWidth() / 2; + int center = left + width / 2; + int minX = center - halfRowWidth; + int maxX = center + halfRowWidth; + var scrollbarPositionX = this.getScrollbarPositionX(); + int m = MathHelper.floor(y - top) - headerHeight + (int) this.getScrollAmount() - 4 + 2; + int entryIndex = m / itemHeight; + return (x < scrollbarPositionX && x >= minX && x <= maxX && entryIndex >= 0 && m >= 0 + && entryIndex < this.getEntryCount() ? this.children().get(entryIndex) : null); + } + + public LanguageOptionsScreen getScreen() { + return screen; } @Override @@ -43,12 +112,7 @@ protected int getScrollbarPositionX() { return right - 6; } - @Override - public void appendNarrations(NarrationMessageBuilder builder) { - var hoveredEntry = getHoveredEntry(); - if (hoveredEntry != null) { - appendNarrations(builder.nextMessage(), hoveredEntry); - hoveredEntry.appendNarrations(builder); - } + protected boolean isFocused() { + return screen.getFocused() == this; } } diff --git a/src/main/java/jerozgen/languagereload/gui/LockedLanguageEntry.java b/src/main/java/jerozgen/languagereload/gui/LockedLanguageEntry.java index e047b3a..8d91047 100644 --- a/src/main/java/jerozgen/languagereload/gui/LockedLanguageEntry.java +++ b/src/main/java/jerozgen/languagereload/gui/LockedLanguageEntry.java @@ -11,9 +11,9 @@ import java.util.function.Consumer; public class LockedLanguageEntry extends LanguageEntry { - private final Text LOCK_BUTTON_TOOLTIP = Text.translatable("language.default.tooltip"); + private static final Text LOCK_BUTTON_TOOLTIP = Text.translatable("language.default.tooltip"); - private final ButtonWidget lockButton = addChild(new TexturedButtonWidget(0, 0, 16, 24, 43, 0, 0, + private final ButtonWidget lockButton = addButton(new TexturedButtonWidget(0, 0, 16, 24, 43, 0, 0, TEXTURE, TEXTURE_WIDTH, TEXTURE_HEIGHT, __ -> {}, new ButtonWidget.TooltipSupplier() { @Override public void onTooltip(ButtonWidget button, MatrixStack matrices, int mouseX, int mouseY) { @@ -28,13 +28,13 @@ public void supply(Consumer consumer) { } }, ScreenTexts.EMPTY)); - public LockedLanguageEntry(Runnable refreshListsAction, LanguageDefinition language, LinkedList selectedLanguages) { - super(refreshListsAction, language, selectedLanguages); + public LockedLanguageEntry(Runnable refreshListsAction, String code, LanguageDefinition language, LinkedList selectedLanguages) { + super(refreshListsAction, code, language, selectedLanguages); lockButton.active = false; } @Override - protected void renderButtons(ButtonRenderer buttonRenderer, int top, int left) { - buttonRenderer.render(lockButton, left + 6, top); + protected void renderButtons(ButtonRenderer renderer, int x, int y) { + renderer.render(lockButton, x + 6, y); } } diff --git a/src/main/java/jerozgen/languagereload/gui/MovableLanguageEntry.java b/src/main/java/jerozgen/languagereload/gui/MovableLanguageEntry.java index f11d1e6..ffc8b6e 100644 --- a/src/main/java/jerozgen/languagereload/gui/MovableLanguageEntry.java +++ b/src/main/java/jerozgen/languagereload/gui/MovableLanguageEntry.java @@ -1,53 +1,72 @@ package jerozgen.languagereload.gui; import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TexturedButtonWidget; import net.minecraft.client.resource.language.LanguageDefinition; import java.util.LinkedList; public class MovableLanguageEntry extends LanguageEntry { - private final ButtonWidget addButton = addChild(createButton(15, 24, 0, 0, __ -> { - selectedLanguages.add(language); - })); + private final ButtonWidget addButton = addButton(15, 24, 0, 0, __ -> add()); + private final ButtonWidget removeButton = addButton(15, 24, 15, 0, __ -> remove()); + private final ButtonWidget moveUpButton = addButton(11, 11, 31, 0, __ -> moveUp()); + private final ButtonWidget moveDownButton = addButton(11, 11, 31, 13, __ -> moveDown()); - private final ButtonWidget removeButton = addChild(createButton(15, 24, 15, 0, __ -> { - selectedLanguages.remove(language); - })); + public MovableLanguageEntry(Runnable refreshListsAction, String code, LanguageDefinition language, LinkedList selectedLanguages) { + super(refreshListsAction, code, language, selectedLanguages); + } - private final ButtonWidget moveUpButton = addChild(createButton(11, 11, 31, 0, __ -> { - var index = selectedLanguages.indexOf(language); - selectedLanguages.add(index - 1, selectedLanguages.remove(index)); - })); + private boolean isSelected() { + return selectedLanguages.contains(code); + } - private final ButtonWidget moveDownButton = addChild(createButton(11, 11, 31, 12, __ -> { - var index = selectedLanguages.indexOf(language); - selectedLanguages.add(index + 1, selectedLanguages.remove(index)); - })); + private boolean isFirst() { + return code.equals(selectedLanguages.peekFirst()); + } + + private boolean isLast() { + return code.equals(selectedLanguages.peekLast()); + } + + private void add() { + selectedLanguages.add(code); + refreshListsAction.run(); + } - public MovableLanguageEntry(Runnable refreshListsAction, LanguageDefinition language, LinkedList selectedLanguages) { - super(refreshListsAction, language, selectedLanguages); + private void remove() { + selectedLanguages.remove(code); + refreshListsAction.run(); } - private ButtonWidget createButton(int width, int height, int u, int v, ButtonWidget.PressAction pressAction) { - return new TexturedButtonWidget(0, 0, width, height, u, v, HOVERED_V_OFFSET, TEXTURE, TEXTURE_WIDTH, TEXTURE_HEIGHT, button -> { - pressAction.onPress(button); - this.refreshListsAction.run(); - }); + @Override + public void toggle() { + if (!isSelected()) add(); + else remove(); + } + + @Override + public void moveUp() { + if (!isSelected()) return; + if (isFirst()) return; + var index = selectedLanguages.indexOf(code); + selectedLanguages.add(index - 1, selectedLanguages.remove(index)); + refreshListsAction.run(); + } + + @Override + public void moveDown() { + if (!isSelected()) return; + if (isLast()) return; + var index = selectedLanguages.indexOf(code); + selectedLanguages.add(index + 1, selectedLanguages.remove(index)); + refreshListsAction.run(); } @Override - protected void renderButtons(ButtonRenderer buttonRenderer, int top, int left) { - if (selectedLanguages.contains(language)) { - buttonRenderer.render(removeButton, left, top); - var mainLanguage = selectedLanguages.peekFirst(); - if (!language.equals(mainLanguage)) { - buttonRenderer.render(moveUpButton, left + removeButton.getWidth() + 1, top); - } - var lastFallback = selectedLanguages.peekLast(); - if (!language.equals(lastFallback)) { - buttonRenderer.render(moveDownButton, left + removeButton.getWidth() + 1, top + moveUpButton.getHeight() + 2); - } - } else buttonRenderer.render(addButton, left + 7, top); + protected void renderButtons(ButtonRenderer renderer, int x, int y) { + if (isSelected()) { + renderer.render(removeButton, x, y); + if (!isFirst()) renderer.render(moveUpButton, x + removeButton.getWidth() + 1, y); + if (!isLast()) renderer.render(moveDownButton, x + removeButton.getWidth() + 1, y + moveUpButton.getHeight() + 2); + } else renderer.render(addButton, x + 7, y); } } diff --git a/src/main/java/jerozgen/languagereload/mixin/AlwaysSelectedEntryListWidgetAccessor.java b/src/main/java/jerozgen/languagereload/mixin/AlwaysSelectedEntryListWidgetAccessor.java new file mode 100644 index 0000000..a20d630 --- /dev/null +++ b/src/main/java/jerozgen/languagereload/mixin/AlwaysSelectedEntryListWidgetAccessor.java @@ -0,0 +1,11 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(AlwaysSelectedEntryListWidget.class) +public interface AlwaysSelectedEntryListWidgetAccessor { + @Accessor + void setInFocus(boolean value); +} diff --git a/src/main/java/jerozgen/languagereload/mixin/EntryListWidgetAccessor.java b/src/main/java/jerozgen/languagereload/mixin/EntryListWidgetAccessor.java new file mode 100644 index 0000000..17ccf3d --- /dev/null +++ b/src/main/java/jerozgen/languagereload/mixin/EntryListWidgetAccessor.java @@ -0,0 +1,11 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.gui.widget.EntryListWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntryListWidget.class) +public interface EntryListWidgetAccessor { + @Accessor + boolean isScrolling(); +} diff --git a/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java b/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java index 400ce32..69c7269 100644 --- a/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java +++ b/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java @@ -8,6 +8,7 @@ import net.minecraft.client.resource.language.LanguageDefinition; import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; +import net.minecraft.text.Texts; import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -35,34 +36,22 @@ private void processLanguageReloadKeys() { if (Screen.hasShiftDown()) { var config = Config.getInstance(); var languageManager = client.getLanguageManager(); - var previousLangCode = config.previousLanguage; - var previousLanguage = languageManager.getLanguage(previousLangCode); - var previousFallbacks = config.previousFallbacks; - if (previousLanguage == null) { + var language = languageManager.getLanguage(config.previousLanguage); + if (language == null) { debugError("debug.reload_languages.switch.failure"); } else { - config.previousFallbacks = config.fallbacks; - config.fallbacks = previousFallbacks; - - config.previousLanguage = languageManager.getLanguage().getCode(); - config.language = previousLangCode; - client.options.language = previousLangCode; - languageManager.setLanguage(previousLanguage); - - LanguageReload.reloadLanguages(); - - var languages = new ArrayList(); - languages.add(previousLanguage.toString()); - languages.addAll(previousFallbacks.stream() - .map(languageManager::getLanguage) - .filter(Objects::nonNull) - .map(LanguageDefinition::toString) - .toList()); - debugLog("debug.reload_languages.switch.success", String.join(", ", languages)); - - client.options.write(); - Config.save(); + LanguageReload.setLanguage(config.previousLanguage, config.previousFallbacks); + var languages = new ArrayList() {{ + add(Text.of(language.toString())); + addAll(config.fallbacks.stream() + .map(languageManager::getLanguage) + .filter(Objects::nonNull) + .map(LanguageDefinition::toString) + .map(Text::of) + .toList()); + }}; + debugLog("debug.reload_languages.switch.success", Texts.join(languages, Text.of(", "))); } } else { LanguageReload.reloadLanguages(); diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java b/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java index 2431a53..b727ea6 100644 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java +++ b/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java @@ -21,8 +21,8 @@ abstract class LanguageManagerMixin { @Shadow public abstract LanguageDefinition getLanguage(String code); - @Inject(method = "reload", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", - remap = false), locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "reload", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", ordinal = 0, + remap = false, target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) void onReload$addFallbacks(ResourceManager manager, CallbackInfo ci, LanguageDefinition languageDefinition, List list) { Lists.reverse(Config.getInstance().fallbacks).stream() .map(this::getLanguage) @@ -38,13 +38,13 @@ abstract class LanguageManagerMixin { LanguageReload.LOGGER.info("Language is not set. Setting it to system language"); var locale = Locale.getDefault(); - var matchingLanguages = languageDefs.values().stream() - .filter(lang -> lang.getCode().split("_")[0].equalsIgnoreCase(locale.getLanguage())) + var matchingLanguages = languageDefs.keySet().stream() + .filter(code -> code.split("_")[0].equalsIgnoreCase(locale.getLanguage())) .toList(); var count = matchingLanguages.size(); if (count > 1) matchingLanguages.stream() - .filter(lang -> { - var split = lang.getCode().split("_"); + .filter(code -> { + var split = code.split("_"); if (split.length < 2) return false; return split[1].equalsIgnoreCase(locale.getCountry()); }) diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java b/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java index b2d0580..b580ce7 100644 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java +++ b/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java @@ -1,17 +1,15 @@ package jerozgen.languagereload.mixin; import jerozgen.languagereload.LanguageReload; +import jerozgen.languagereload.access.ILanguageOptionsScreen; import jerozgen.languagereload.config.Config; -import jerozgen.languagereload.gui.LanguageListWidget; -import jerozgen.languagereload.gui.LockedLanguageEntry; -import jerozgen.languagereload.gui.MovableLanguageEntry; +import jerozgen.languagereload.gui.*; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.GameOptionsScreen; import net.minecraft.client.gui.screen.option.LanguageOptionsScreen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.option.GameOptions; -import net.minecraft.client.resource.language.LanguageDefinition; import net.minecraft.client.resource.language.LanguageManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.screen.ScreenTexts; @@ -24,18 +22,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.*; -import java.util.stream.Collectors; +import java.util.stream.Stream; @Mixin(LanguageOptionsScreen.class) -public abstract class LanguageOptionsScreenMixin extends GameOptionsScreen { +public abstract class LanguageOptionsScreenMixin extends GameOptionsScreen implements ILanguageOptionsScreen { @Shadow @Final private static Text LANGUAGE_WARNING_TEXT; - @Shadow @Final LanguageManager languageManager; private LanguageListWidget availableLanguageList; private LanguageListWidget selectedLanguageList; private TextFieldWidget searchBox; - private final LinkedList selectedLanguages = new LinkedList<>(); - private final Map languageEntries = new LinkedHashMap<>(); + private final LinkedList selectedLanguages = new LinkedList<>(); + private final Map languageEntries = new LinkedHashMap<>(); private LockedLanguageEntry defaultLanguageEntry; LanguageOptionsScreenMixin(Screen parent, GameOptions options, Text title) { @@ -44,34 +41,38 @@ public abstract class LanguageOptionsScreenMixin extends GameOptionsScreen { @Inject(method = "", at = @At("TAIL")) void onConstructed(Screen parent, GameOptions options, LanguageManager languageManager, CallbackInfo ci) { - var currentLang = languageManager.getLanguage(); - if (!currentLang.getCode().equals(LanguageManager.DEFAULT_LANGUAGE_CODE)) selectedLanguages.add(currentLang); - - var fallbacks = Config.getInstance().fallbacks.stream() - .map(languageManager::getLanguage) - .filter(Objects::nonNull) - .toList(); - selectedLanguages.addAll(fallbacks); - + var currentLangCode = languageManager.getLanguage().getCode(); + if (!currentLangCode.equals(LanguageManager.DEFAULT_LANGUAGE_CODE)) + selectedLanguages.add(currentLangCode); + selectedLanguages.addAll(Config.getInstance().fallbacks); for (var language : languageManager.getAllLanguages()) { - if (!language.getCode().equals(LanguageManager.DEFAULT_LANGUAGE_CODE)) { - languageEntries.put(language, new MovableLanguageEntry(this::refresh, language, selectedLanguages)); - } else { - defaultLanguageEntry = new LockedLanguageEntry(this::refresh, language, selectedLanguages); - } + var code = language.getCode(); + if (!code.equals(LanguageManager.DEFAULT_LANGUAGE_CODE)) + languageEntries.put(code, new MovableLanguageEntry(this::refresh, code, language, selectedLanguages)); + else defaultLanguageEntry = new LockedLanguageEntry(this::refresh, code, language, selectedLanguages); } } @Inject(method = "init", at = @At("HEAD"), cancellable = true) void onInit(CallbackInfo ci) { - searchBox = new TextFieldWidget(textRenderer, width / 2 - 100, 22, 200, 20, searchBox, Text.empty()); + searchBox = new TextFieldWidget(textRenderer, width / 2 - 100, 22, 200, 20, searchBox, Text.empty()) { + @Override + public void setFocused(boolean focused) { + if (!isFocused() && focused) { + super.setFocused(true); + focusSearch(); + } + else super.setFocused(focused); + } + }; searchBox.setChangedListener(__ -> refresh()); addSelectableChild(searchBox); setInitialFocus(searchBox); var listWidth = Math.min(width / 2 - 4, 200); - availableLanguageList = new LanguageListWidget(client, listWidth, height, Text.translatable("pack.available.title")); - selectedLanguageList = new LanguageListWidget(client, listWidth, height, Text.translatable("pack.selected.title")); + var it = (LanguageOptionsScreen) (Object) this; + availableLanguageList = new LanguageListWidget(client, it, listWidth, height, Text.translatable("pack.available.title")); + selectedLanguageList = new LanguageListWidget(client, it, listWidth, height, Text.translatable("pack.selected.title")); availableLanguageList.setLeftPos(width / 2 - 4 - listWidth); selectedLanguageList.setLeftPos(width / 2 + 4); addSelectableChild(availableLanguageList); @@ -90,34 +91,56 @@ private void onDone(ButtonWidget button) { client.setScreen(parent); var language = selectedLanguages.peekFirst(); - if (language == null) language = languageManager.getLanguage(LanguageManager.DEFAULT_LANGUAGE_CODE); - var fallbacks = selectedLanguages.stream() - .skip(1) - .map(LanguageDefinition::getCode) - .collect(Collectors.toCollection(LinkedList::new)); + if (language == null) { + LanguageReload.setLanguage(LanguageManager.DEFAULT_LANGUAGE_CODE, new LinkedList<>()); + } else { + var fallbacks = new LinkedList<>(selectedLanguages); + fallbacks.remove(0); + LanguageReload.setLanguage(language, fallbacks); + } + } - LanguageReload.setLanguage(language, fallbacks); + @Override + public void focusEntry(LanguageEntry entry) { + ((AlwaysSelectedEntryListWidgetAccessor) availableLanguageList).setInFocus(false); + ((AlwaysSelectedEntryListWidgetAccessor) selectedLanguageList).setInFocus(false); + entry.getParent().setSelected(entry); + entry.getParent().changeFocus(true); + setFocused(entry.getParent()); + } + + public void focusSearch() { + ((AlwaysSelectedEntryListWidgetAccessor) availableLanguageList).setInFocus(false); + ((AlwaysSelectedEntryListWidgetAccessor) selectedLanguageList).setInFocus(false); + setFocused(searchBox); } private void refresh() { - selectedLanguageList.children().clear(); - selectedLanguages.forEach(language -> { - var entry = languageEntries.get(language); - if (entry != null) selectedLanguageList.children().add(entry); - }); - selectedLanguageList.children().add(defaultLanguageEntry); - - availableLanguageList.children().clear(); - languageEntries.forEach((lang, entry) -> { - if (selectedLanguageList.children().contains(entry)) return; - var langName = entry.getLanguage().toString().toLowerCase(Locale.ROOT); - var langCode = entry.getLanguage().getCode().toLowerCase(Locale.ROOT); - var query = searchBox.getText().toLowerCase(Locale.ROOT); - if (langName.contains(query) || langCode.contains(query)) { - availableLanguageList.children().add(entry); + refreshList(selectedLanguageList, Stream.concat( + selectedLanguages.stream().map(languageEntries::get).filter(Objects::nonNull), + Stream.of(defaultLanguageEntry))); + refreshList(availableLanguageList, languageEntries.values().stream() + .filter(entry -> { + if (selectedLanguageList.children().contains(entry)) return false; + var query = searchBox.getText().toLowerCase(Locale.ROOT); + var langCode = entry.getCode().toLowerCase(Locale.ROOT); + var langName = entry.getLanguage().toString().toLowerCase(Locale.ROOT); + return langCode.contains(query) || langName.contains(query); + })); + } + + private void refreshList(LanguageListWidget list, Stream entries) { + var selectedEntry = list.getSelectedOrNull(); + list.setSelected(null); + list.children().clear(); + entries.forEach(entry -> { + list.children().add(entry); + entry.setParent(list); + if (entry == selectedEntry) { + list.setSelected(entry); } }); - availableLanguageList.setScrollAmount(availableLanguageList.getScrollAmount()); + list.setScrollAmount(list.getScrollAmount()); } @Inject(method = "render", at = @At("HEAD"), cancellable = true) diff --git a/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java b/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java index 1cebae4..24cbdb2 100644 --- a/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java +++ b/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java @@ -2,27 +2,19 @@ import jerozgen.languagereload.LanguageReload; import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; -import net.minecraft.client.resource.language.LanguageDefinition; -import net.minecraft.client.resource.language.LanguageManager; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.LinkedList; @Mixin(RecipeBookWidget.class) public class RecipeBookWidgetMixin { - @Redirect(method = "triggerPirateSpeakEasterEgg", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/resource/language/LanguageManager;setLanguage(Lnet/minecraft/client/resource/language/LanguageDefinition;)V")) - void onLanguageSwitching$redirect(LanguageManager instance, LanguageDefinition language) { - LanguageReload.setLanguage(language, new LinkedList<>()); - } - - @Inject(method = "triggerPirateSpeakEasterEgg", cancellable = true, at = @At(value = "INVOKE", shift = At.Shift.AFTER, + @Inject(method = "triggerPirateSpeakEasterEgg", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resource/language/LanguageManager;setLanguage(Lnet/minecraft/client/resource/language/LanguageDefinition;)V")) void onLanguageSwitching$cancel(String search, CallbackInfo ci) { + LanguageReload.setLanguage("en_pt", new LinkedList<>()); ci.cancel(); } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index c0b7591..5757449 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -41,8 +41,7 @@ "depends": { "fabric-resource-loader-v0": "*", "fabricloader": ">=0.14.6", - "minecraft": ">=1.19 <1.19.3", - "java": ">=17" + "minecraft": ">=1.19 <1.19.3" }, "suggests": { "modmenu": "*" diff --git a/src/main/resources/languagereload.accesswidener b/src/main/resources/languagereload.accesswidener index 66e2256..d0c701e 100644 --- a/src/main/resources/languagereload.accesswidener +++ b/src/main/resources/languagereload.accesswidener @@ -2,3 +2,5 @@ accessWidener v1 named accessible class net/minecraft/client/gui/screen/option/LanguageOptionsScreen$LanguageSelectionListWidget extendable class net/minecraft/client/world/ClientChunkManager$ClientChunkMap + +extendable method net/minecraft/client/gui/widget/EntryListWidget getEntryAtPosition (DD)Lnet/minecraft/client/gui/widget/EntryListWidget$Entry; diff --git a/src/main/resources/languagereload.mixins.json b/src/main/resources/languagereload.mixins.json index 4bbd63e..52a37a6 100644 --- a/src/main/resources/languagereload.mixins.json +++ b/src/main/resources/languagereload.mixins.json @@ -7,9 +7,11 @@ "AdvancementsScreenMixin", "AdvancementTabMixin", "AdvancementWidgetAccessor", + "AlwaysSelectedEntryListWidgetAccessor", "BookScreenAccessor", "ClientChunkManagerAccessor", "ClientChunkMapAccessor", + "EntryListWidgetAccessor", "GameOptionsMixin", "KeyboardMixin", "LanguageManagerMixin",