diff --git a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java index 457e28e319..d1214aabe4 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java @@ -337,7 +337,7 @@ private void screenHandlerListW(WTable table, ScreenHandlerListSetting setting) private void blockDataW(WTable table, BlockDataSetting setting) { WButton button = table.add(theme.button(GuiRenderer.EDIT)).expandCellX().widget(); - button.action = () -> mc.setScreen(new BlockDataSettingScreen(theme, setting)); + button.action = () -> mc.setScreen(new BlockDataSettingScreen<>(theme, setting)); reset(table, setting, null); } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java index f9896d0699..23b86b2f6b 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockDataSettingScreen.java @@ -6,10 +6,9 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; -import meteordevelopment.meteorclient.gui.WindowScreen; import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; -import meteordevelopment.meteorclient.gui.widgets.containers.WTable; -import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionMapSettingScreen; +import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; import meteordevelopment.meteorclient.settings.BlockDataSetting; import meteordevelopment.meteorclient.settings.IBlockData; @@ -18,84 +17,48 @@ import meteordevelopment.meteorclient.utils.misc.ISerializable; import meteordevelopment.meteorclient.utils.misc.Names; import net.minecraft.block.Block; +import net.minecraft.block.Blocks; import net.minecraft.registry.Registries; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.List; +import org.jetbrains.annotations.Nullable; import static meteordevelopment.meteorclient.MeteorClient.mc; -public class BlockDataSettingScreen extends WindowScreen { - private static final List BLOCKS = new ArrayList<>(100); - - private final BlockDataSetting setting; - - private WTable table; - private String filterText = ""; +public class BlockDataSettingScreen & ISerializable & IChangeable & IBlockData> extends CollectionMapSettingScreen { + private final BlockDataSetting setting; - public BlockDataSettingScreen(GuiTheme theme, BlockDataSetting setting) { - super(theme, "Configure Blocks"); + public BlockDataSettingScreen(GuiTheme theme, BlockDataSetting setting) { + super(theme, "Configure Blocks", setting, setting.get(), Registries.BLOCK); this.setting = setting; } @Override - public void initWidgets() { - WTextBox filter = add(theme.textBox("")).minWidth(400).expandX().widget(); - filter.setFocused(true); - filter.action = () -> { - filterText = filter.get().trim(); - - table.clear(); - initTable(); - }; - - table = add(theme.table()).expandX().widget(); - - initTable(); + protected boolean includeValue(Block value) { + return value != Blocks.AIR; } - @SuppressWarnings("unchecked") - public & ISerializable & IChangeable & IBlockData> void initTable() { - for (Block block : Registries.BLOCK) { - T blockData = (T) setting.get().get(block); - - if (blockData != null && blockData.isChanged()) BLOCKS.addFirst(block); - else BLOCKS.add(block); - } - - for (Block block : BLOCKS) { - String name = Names.get(block); - if (!StringUtils.containsIgnoreCase(name, filterText)) continue; - - T blockData = (T) setting.get().get(block); - - table.add(theme.itemWithLabel(block.asItem().getDefaultStack(), Names.get(block))).expandCellX(); - table.add(theme.label((blockData != null && blockData.isChanged()) ? "*" : " ")); - - WButton edit = table.add(theme.button(GuiRenderer.EDIT)).widget(); - edit.action = () -> { - T data = blockData; - if (data == null) data = (T) setting.defaultData.get().copy(); - - mc.setScreen(data.createScreen(theme, block, (BlockDataSetting) setting)); - }; - - WButton reset = table.add(theme.button(GuiRenderer.RESET)).widget(); - reset.action = () -> { - setting.get().remove(block); - setting.onChanged(); + @Override + protected WWidget getValueWidget(Block block) { + return theme.itemWithLabel(block.asItem().getDefaultStack(), Names.get(block)); + } - if (blockData != null && blockData.isChanged()) { - table.clear(); - initTable(); - } - }; + @Override + protected WWidget getDataWidget(Block block, @Nullable T blockData) { + WButton edit = theme.button(GuiRenderer.EDIT); + edit.action = () -> { + T data = blockData; + if (data == null) data = setting.defaultData.get().copy(); - table.row(); - } + mc.setScreen(data.createScreen(theme, block, setting)); + }; + return edit; + } - BLOCKS.clear(); + @Override + protected String[] getValueNames(Block block) { + return new String[]{ + Names.get(block), + Registries.BLOCK.getId(block).toString() + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java index 88000a2602..d0309ce85b 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/BlockListSettingScreen.java @@ -6,28 +6,28 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; -import meteordevelopment.meteorclient.mixin.IdentifierAccessor; import meteordevelopment.meteorclient.settings.BlockListSetting; -import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.utils.misc.Names; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.registry.Registries; import net.minecraft.util.Identifier; -import java.util.List; import java.util.function.Predicate; public class BlockListSettingScreen extends CollectionListSettingScreen { - private static final Identifier ID = Identifier.of("minecraft", ""); - - public BlockListSettingScreen(GuiTheme theme, Setting> setting) { + public BlockListSettingScreen(GuiTheme theme, BlockListSetting setting) { super(theme, "Select Blocks", setting, setting.get(), Registries.BLOCK); } @Override protected boolean includeValue(Block value) { + if (Registries.BLOCK.getId(value).getPath().endsWith("_wall_banner")) { + return false; + } + Predicate filter = ((BlockListSetting) setting).filter; if (filter == null) return value != Blocks.AIR; @@ -36,17 +36,15 @@ protected boolean includeValue(Block value) { @Override protected WWidget getValueWidget(Block value) { - return theme.itemWithLabel(value.asItem().getDefaultStack(), getValueName(value)); - } - - @Override - protected String getValueName(Block value) { - return Names.get(value); + return theme.itemWithLabel(value.asItem().getDefaultStack(), Names.get(value)); } @Override - protected boolean skipValue(Block value) { - return Registries.BLOCK.getId(value).getPath().endsWith("_wall_banner"); + protected String[] getValueNames(Block value) { + return new String[]{ + Names.get(value), + Registries.BLOCK.getId(value).toString() + }; } @Override @@ -54,7 +52,6 @@ protected Block getAdditionalValue(Block value) { String path = Registries.BLOCK.getId(value).getPath(); if (!path.endsWith("_banner")) return null; - ((IdentifierAccessor) (Object) ID).setPath(path.substring(0, path.length() - 6) + "wall_banner"); - return Registries.BLOCK.get(ID); + return Registries.BLOCK.get(Identifier.ofVanilla(path.substring(0, path.length() - 6) + "wall_banner")); } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/DynamicRegistryListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/DynamicRegistryListSettingScreen.java deleted file mode 100644 index a0d13065c4..0000000000 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/DynamicRegistryListSettingScreen.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). - * Copyright (c) Meteor Development. - */ - -package meteordevelopment.meteorclient.gui.screens.settings; - -import meteordevelopment.meteorclient.gui.GuiTheme; -import meteordevelopment.meteorclient.gui.WindowScreen; -import meteordevelopment.meteorclient.gui.utils.Cell; -import meteordevelopment.meteorclient.gui.widgets.WWidget; -import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList; -import meteordevelopment.meteorclient.gui.widgets.containers.WTable; -import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; -import meteordevelopment.meteorclient.gui.widgets.pressable.WPressable; -import meteordevelopment.meteorclient.settings.Setting; -import meteordevelopment.meteorclient.utils.Utils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.util.Identifier; -import net.minecraft.util.InvalidIdentifierException; -import net.minecraft.util.Pair; - -import java.util.*; -import java.util.function.Consumer; - -public abstract class DynamicRegistryListSettingScreen extends WindowScreen { - protected final Setting setting; - protected final Collection> collection; - private final RegistryKey> registryKey; - private final Optional> registry; - - private WTextBox filter; - private String filterText = ""; - - private WTable table; - - public DynamicRegistryListSettingScreen(GuiTheme theme, String title, Setting setting, Collection> collection, RegistryKey> registryKey) { - super(theme, title); - - this.registryKey = registryKey; - this.registry = Optional.ofNullable(MinecraftClient.getInstance().getNetworkHandler()) - .flatMap(networkHandler -> networkHandler.getRegistryManager().getOptional(registryKey)); - this.setting = setting; - this.collection = collection; - } - - @Override - public void initWidgets() { - // Filter - filter = add(theme.textBox("")).minWidth(400).expandX().widget(); - filter.setFocused(true); - filter.action = () -> { - filterText = filter.get().trim(); - - table.clear(); - generateWidgets(); - }; - - table = add(theme.table()).expandX().widget(); - - generateWidgets(); - } - - private void generateWidgets() { - // Left (all) - WTable left = abc(pairs -> registry.ifPresent(registry -> { - registry.streamEntries() - .map(RegistryEntry.Reference::getKey) - .filter(Optional::isPresent) - .map(Optional::get).forEach(t -> { - if (skipValue(t) || collection.contains(t)) return; - - int words = Utils.searchInWords(getValueName(t), filterText); - int diff = Utils.searchLevenshteinDefault(getValueName(t), filterText, false); - if (words > 0 || diff <= getValueName(t).length() / 2) pairs.add(new Pair<>(t, -diff)); - }); - }), true, t -> { - addValue(t); - - RegistryKey v = getAdditionalValue(t); - if (v != null) addValue(v); - } - ); - - if (!left.cells.isEmpty()) { - left.add(theme.horizontalSeparator()).expandX(); - left.row(); - } - - WHorizontalList manualEntry = left.add(theme.horizontalList()).expandX().widget(); - WTextBox textBox = manualEntry.add(theme.textBox("minecraft:")).expandX().minWidth(120d).widget(); - manualEntry.add(theme.plus()).expandCellX().right().widget().action = () -> { - String entry = textBox.get().trim(); - try { - Identifier id = entry.contains(":") ? Identifier.of(entry) : Identifier.ofVanilla(entry); - addValue(RegistryKey.of(registryKey, id)); - } catch (InvalidIdentifierException ignored) {} - }; - - table.add(theme.verticalSeparator()).expandWidgetY(); - - // Right (selected) - abc(pairs -> { - for (RegistryKey value : collection) { - if (skipValue(value)) continue; - - int words = Utils.searchInWords(getValueName(value), filterText); - int diff = Utils.searchLevenshteinDefault(getValueName(value), filterText, false); - if (words > 0 || diff <= getValueName(value).length() / 2) pairs.add(new Pair<>(value, -diff)); - } - }, false, t -> { - removeValue(t); - - RegistryKey v = getAdditionalValue(t); - if (v != null) removeValue(v); - }); - } - - private void addValue(RegistryKey value) { - if (!collection.contains(value)) { - collection.add(value); - - setting.onChanged(); - table.clear(); - generateWidgets(); - } - } - - private void removeValue(RegistryKey value) { - if (collection.remove(value)) { - setting.onChanged(); - table.clear(); - generateWidgets(); - } - } - - private WTable abc(Consumer, Integer>>> addValues, boolean isLeft, Consumer> buttonAction) { - // Create - Cell cell = this.table.add(theme.table()).top(); - WTable table = cell.widget(); - - Consumer> forEach = t -> { - if (!includeValue(t)) return; - - table.add(getValueWidget(t)); - - WPressable button = table.add(isLeft ? theme.plus() : theme.minus()).expandCellX().right().widget(); - button.action = () -> buttonAction.accept(t); - - table.row(); - }; - - // Sort - List, Integer>> values = new ArrayList<>(); - addValues.accept(values); - if (!filterText.isEmpty()) values.sort(Comparator.comparingInt(value -> -value.getRight())); - for (Pair, Integer> pair : values) forEach.accept(pair.getLeft()); - - if (!table.cells.isEmpty()) cell.expandX(); - - return table; - } - - protected boolean includeValue(RegistryKey value) { - return true; - } - - protected abstract WWidget getValueWidget(RegistryKey value); - - protected abstract String getValueName(RegistryKey value); - - protected boolean skipValue(RegistryKey value) { - return false; - } - - protected RegistryKey getAdditionalValue(RegistryKey value) { - return null; - } -} diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/EnchantmentListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/EnchantmentListSettingScreen.java index 7ffdddd6bc..f6dad94ea0 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/EnchantmentListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/EnchantmentListSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.DynamicRegistryListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.utils.misc.Names; @@ -22,11 +23,14 @@ public EnchantmentListSettingScreen(GuiTheme theme, Setting value) { - return theme.label(getValueName(value)); + return theme.label(Names.get(value)); } @Override - protected String getValueName(RegistryKey value) { - return Names.get(value); + protected String[] getValueNames(RegistryKey value) { + return new String[]{ + Names.get(value), + value.getValue().toString() + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ItemListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ItemListSettingScreen.java index b73d6950da..3e58d7d70f 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ItemListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ItemListSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.ItemListSetting; import meteordevelopment.meteorclient.utils.misc.Names; @@ -34,7 +35,10 @@ protected WWidget getValueWidget(Item value) { } @Override - protected String getValueName(Item value) { - return Names.get(value); + protected String[] getValueNames(Item value) { + return new String[]{ + Names.get(value), + Registries.ITEM.getId(value).toString() + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ModuleListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ModuleListSettingScreen.java index e00d03f37a..b8b3ab4fbf 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ModuleListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ModuleListSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.systems.modules.Module; @@ -20,11 +21,14 @@ public ModuleListSettingScreen(GuiTheme theme, Setting> setting) { @Override protected WWidget getValueWidget(Module value) { - return theme.label(getValueName(value)); + return theme.label(value.title); } @Override - protected String getValueName(Module value) { - return value.title; + protected String[] getValueNames(Module value) { + String[] names = new String[value.aliases.length + 1]; + System.arraycopy(value.aliases, 0, names, 1, value.aliases.length); + names[0] = value.title; + return names; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/PacketBoolSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/PacketBoolSettingScreen.java index c4398694b1..ee765c1dc0 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/PacketBoolSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/PacketBoolSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.PacketListSetting; import meteordevelopment.meteorclient.settings.Setting; @@ -30,11 +31,14 @@ protected boolean includeValue(Class> value) { @Override protected WWidget getValueWidget(Class> value) { - return theme.label(getValueName(value)); + return theme.label(PacketUtils.getName(value)); } @Override - protected String getValueName(Class> value) { - return PacketUtils.getName(value); + protected String[] getValueNames(Class> value) { + return new String[]{ + PacketUtils.getName(value), + value.getSimpleName() + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ParticleTypeListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ParticleTypeListSettingScreen.java index a44f6c6823..ae2cc22806 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ParticleTypeListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ParticleTypeListSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.utils.misc.Names; @@ -21,17 +22,20 @@ public ParticleTypeListSettingScreen(GuiTheme theme, Setting value) { - return theme.label(getValueName(value)); + protected boolean includeValue(ParticleType value) { + return value instanceof ParticleEffect; } @Override - protected String getValueName(ParticleType value) { - return Names.get(value); + protected WWidget getValueWidget(ParticleType value) { + return theme.label(Names.get(value)); } @Override - protected boolean skipValue(ParticleType value) { - return !(value instanceof ParticleEffect); + protected String[] getValueNames(ParticleType value) { + return new String[]{ + Names.get(value), + Registries.PARTICLE_TYPE.getId(value).toString() + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ScreenHandlerSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ScreenHandlerSettingScreen.java index 75f6b1e53c..c779f4b37a 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ScreenHandlerSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/ScreenHandlerSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import net.minecraft.registry.Registries; @@ -20,11 +21,17 @@ public ScreenHandlerSettingScreen(GuiTheme theme, Setting value) { - return theme.label(getValueName(value)); + return theme.label(getName(value)); } @Override - protected String getValueName(ScreenHandlerType type) { + protected String[] getValueNames(ScreenHandlerType type) { + return new String[]{ + getName(type) + }; + } + + private static String getName(ScreenHandlerType type) { return Registries.SCREEN_HANDLER.getId(type).toString(); } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/SoundEventListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/SoundEventListSettingScreen.java index d28f1056bb..f94bdbfd15 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/SoundEventListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/SoundEventListSettingScreen.java @@ -6,8 +6,10 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; +import net.minecraft.client.resource.language.I18n; import net.minecraft.registry.Registries; import net.minecraft.sound.SoundEvent; @@ -20,11 +22,14 @@ public SoundEventListSettingScreen(GuiTheme theme, Setting> set @Override protected WWidget getValueWidget(SoundEvent value) { - return theme.label(getValueName(value)); + return theme.label(value.id().getPath()); } @Override - protected String getValueName(SoundEvent value) { - return value.id().getPath(); + protected String[] getValueNames(SoundEvent value) { + return new String[]{ + value.id().toString(), + I18n.translate("subtitles." + value.id().getPath()) + }; } } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StatusEffectListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StatusEffectListSettingScreen.java index 10bd546e28..608c035a6b 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StatusEffectListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StatusEffectListSettingScreen.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.gui.screens.settings; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.utils.misc.Names; @@ -26,12 +27,15 @@ public StatusEffectListSettingScreen(GuiTheme theme, Setting> @Override protected WWidget getValueWidget(StatusEffect value) { - return theme.itemWithLabel(getPotionStack(value), getValueName(value)); + return theme.itemWithLabel(getPotionStack(value), Names.get(value)); } @Override - protected String getValueName(StatusEffect value) { - return Names.get(value); + protected String[] getValueNames(StatusEffect value) { + return new String[]{ + Names.get(value), + Registries.STATUS_EFFECT.getId(value).toString() + }; } private ItemStack getPotionStack(StatusEffect effect) { diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StorageBlockListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StorageBlockListSettingScreen.java index 5a0d50aaf6..f73d2cfb9e 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StorageBlockListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/StorageBlockListSettingScreen.java @@ -7,12 +7,14 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.screens.settings.base.CollectionListSettingScreen; import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.settings.StorageBlockListSetting; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.item.Item; import net.minecraft.item.Items; +import net.minecraft.registry.Registries; import java.util.List; import java.util.Map; @@ -47,13 +49,16 @@ public StorageBlockListSettingScreen(GuiTheme theme, Setting value) { - Item item = BLOCK_ENTITY_TYPE_INFO_MAP.getOrDefault(value, UNKNOWN).item(); - return theme.itemWithLabel(item.getDefaultStack(), getValueName(value)); + BlockEntityTypeInfo info = BLOCK_ENTITY_TYPE_INFO_MAP.getOrDefault(value, UNKNOWN); + return theme.itemWithLabel(info.item().getDefaultStack(), info.name()); } @Override - protected String getValueName(BlockEntityType value) { - return BLOCK_ENTITY_TYPE_INFO_MAP.getOrDefault(value, UNKNOWN).name(); + protected String[] getValueNames(BlockEntityType value) { + return new String[]{ + BLOCK_ENTITY_TYPE_INFO_MAP.getOrDefault(value, UNKNOWN).name(), + Registries.BLOCK_ENTITY_TYPE.getId(value).toString() + }; } private record BlockEntityTypeInfo(Item item, String name) {} diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/CollectionListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionListSettingScreen.java similarity index 54% rename from src/main/java/meteordevelopment/meteorclient/gui/screens/settings/CollectionListSettingScreen.java rename to src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionListSettingScreen.java index 70511658bf..cf846c58d8 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/CollectionListSettingScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionListSettingScreen.java @@ -3,7 +3,7 @@ * Copyright (c) Meteor Development. */ -package meteordevelopment.meteorclient.gui.screens.settings; +package meteordevelopment.meteorclient.gui.screens.settings.base; import meteordevelopment.meteorclient.gui.GuiTheme; import meteordevelopment.meteorclient.gui.WindowScreen; @@ -13,24 +13,18 @@ import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; import meteordevelopment.meteorclient.gui.widgets.pressable.WPressable; import meteordevelopment.meteorclient.settings.Setting; -import meteordevelopment.meteorclient.utils.Utils; -import net.minecraft.util.Pair; -import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; -import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; public abstract class CollectionListSettingScreen extends WindowScreen { protected final Setting setting; protected final Collection collection; private final Iterable registry; - private WTextBox filter; - private String filterText = ""; - private WTable table; + private String filterText = ""; public CollectionListSettingScreen(GuiTheme theme, String title, Setting setting, Collection collection, Iterable registry) { super(theme, title); @@ -43,110 +37,95 @@ public CollectionListSettingScreen(GuiTheme theme, String title, Setting sett @Override public void initWidgets() { // Filter - filter = add(theme.textBox("")).minWidth(400).expandX().widget(); + WTextBox filter = add(theme.textBox("")).minWidth(400).expandX().widget(); filter.setFocused(true); filter.action = () -> { filterText = filter.get().trim(); table.clear(); - initWidgets(registry); + initTable(); }; table = add(theme.table()).expandX().widget(); - initWidgets(registry); + initTable(); } - private void initWidgets(Iterable registry) { + private void initTable() { // Left (all) - WTable left = abc(pairs -> registry.forEach(t -> { - if (skipValue(t) || collection.contains(t)) return; - - int words = Utils.searchInWords(getValueName(t), filterText); - int diff = Utils.searchLevenshteinDefault(getValueName(t), filterText, false); - if (words > 0 || diff <= getValueName(t).length() / 2) pairs.add(new Pair<>(t, -diff)); - }), true, t -> { - addValue(registry, t); + WTable left = abc(registry, true, t -> { + addValue(t); T v = getAdditionalValue(t); - if (v != null) addValue(registry, v); + if (v != null) addValue(v); }); if (!left.cells.isEmpty()) table.add(theme.verticalSeparator()).expandWidgetY(); // Right (selected) - abc(pairs -> { - for (T value : collection) { - if (skipValue(value)) continue; - - int words = Utils.searchInWords(getValueName(value), filterText); - int diff = Utils.searchLevenshteinDefault(getValueName(value), filterText, false); - if (words > 0 || diff <= getValueName(value).length() / 2) pairs.add(new Pair<>(value, -diff)); - } - }, false, t -> { - removeValue(registry, t); + WTable right = abc(collection, false, t -> { + removeValue(t); T v = getAdditionalValue(t); - if (v != null) removeValue(registry, v); + if (v != null) removeValue(v); }); - } - private void addValue(Iterable registry, T value) { - if (!collection.contains(value)) { - collection.add(value); - - setting.onChanged(); - table.clear(); - initWidgets(registry); - } - } - - private void removeValue(Iterable registry, T value) { - if (collection.remove(value)) { - setting.onChanged(); - table.clear(); - initWidgets(registry); - } + postWidgets(left, right); } - private WTable abc(Consumer>> addValues, boolean isLeft, Consumer buttonAction) { + private WTable abc(Iterable iterable, boolean isLeft, Consumer buttonAction) { // Create Cell cell = this.table.add(theme.table()).top(); WTable table = cell.widget(); - Consumer forEach = t -> { - if (!includeValue(t)) return; + // Sort + Predicate predicate = isLeft + ? value -> this.includeValue(value) && !collection.contains(value) + : this::includeValue; + Iterable sorted = SortingHelper.sort(iterable, predicate, this::getValueNames, filterText); + + sorted.forEach(t -> { table.add(getValueWidget(t)); WPressable button = table.add(isLeft ? theme.plus() : theme.minus()).expandCellX().right().widget(); button.action = () -> buttonAction.accept(t); table.row(); - }; - - // Sort - List> values = new ArrayList<>(); - addValues.accept(values); - if (!filterText.isEmpty()) values.sort(Comparator.comparingInt(value -> -value.getRight())); - for (Pair pair : values) forEach.accept(pair.getLeft()); + }); if (!table.cells.isEmpty()) cell.expandX(); return table; } + protected void addValue(T value) { + if (!collection.contains(value)) { + collection.add(value); + + setting.onChanged(); + table.clear(); + initWidgets(); + } + } + + protected void removeValue(T value) { + if (collection.remove(value)) { + setting.onChanged(); + table.clear(); + initWidgets(); + } + } + + protected void postWidgets(WTable left, WTable right) {} + protected boolean includeValue(T value) { return true; } protected abstract WWidget getValueWidget(T value); - protected abstract String getValueName(T value); - - protected boolean skipValue(T value) { - return false; - } + protected abstract String[] getValueNames(T value); protected T getAdditionalValue(T value) { return null; diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionMapSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionMapSettingScreen.java new file mode 100644 index 0000000000..5ca289a35b --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/CollectionMapSettingScreen.java @@ -0,0 +1,91 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.gui.screens.settings.base; + +import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.WindowScreen; +import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; +import meteordevelopment.meteorclient.gui.widgets.WWidget; +import meteordevelopment.meteorclient.gui.widgets.containers.WTable; +import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; +import meteordevelopment.meteorclient.settings.Setting; +import meteordevelopment.meteorclient.utils.misc.IChangeable; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.Map; + +public abstract class CollectionMapSettingScreen extends WindowScreen { + private final Setting setting; + protected final Map map; + private final Iterable registry; + + private WTable table; + private String filterText = ""; + + public CollectionMapSettingScreen(GuiTheme theme, String title, Setting setting, Map map, Iterable registry) { + super(theme, title); + + this.setting = setting; + this.map = map; + this.registry = registry; + } + + @Override + public void initWidgets() { + // Filter + WTextBox filter = add(theme.textBox("")).minWidth(400).expandX().widget(); + filter.setFocused(true); + filter.action = () -> { + filterText = filter.get().trim(); + + table.clear(); + initTable(); + }; + + table = add(theme.table()).expandX().widget(); + + initTable(); + } + + private void initTable() { + Comparator prioritizeChanged = Comparator.comparing(key -> !(map.get(key) instanceof IChangeable changeable && changeable.isChanged())); + Iterable sorted = SortingHelper.sortWithPriority(registry, this::includeValue, this::getValueNames, filterText, prioritizeChanged); + + sorted.forEach(t -> { + @Nullable V data = map.get(t); + boolean isChanged = data instanceof IChangeable changeable && changeable.isChanged(); + + table.add(getValueWidget(t)).expandCellX(); + table.add(theme.label(isChanged ? "*" : " ")); + table.add(getDataWidget(t, data)); + + table.add(theme.button(GuiRenderer.RESET)).widget().action = () -> { + removeValue(t); + }; + + table.row(); + }); + } + + protected void removeValue(K value) { + if (map.remove(value) != null) { + setting.onChanged(); + table.clear(); + initWidgets(); + } + } + + protected boolean includeValue(K value) { + return true; + } + + protected abstract WWidget getValueWidget(K value); + + protected abstract WWidget getDataWidget(K value, @Nullable V data); + + protected abstract String[] getValueNames(K value); +} diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/DynamicRegistryListSettingScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/DynamicRegistryListSettingScreen.java new file mode 100644 index 0000000000..f151dddea3 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/DynamicRegistryListSettingScreen.java @@ -0,0 +1,61 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.gui.screens.settings.base; + +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList; +import meteordevelopment.meteorclient.gui.widgets.containers.WTable; +import meteordevelopment.meteorclient.gui.widgets.input.WTextBox; +import meteordevelopment.meteorclient.settings.Setting; +import net.minecraft.client.MinecraftClient; +import net.minecraft.registry.*; +import net.minecraft.util.Identifier; +import net.minecraft.util.InvalidIdentifierException; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +public abstract class DynamicRegistryListSettingScreen extends CollectionListSettingScreen> { + protected final RegistryKey> registryKey; + + public DynamicRegistryListSettingScreen(GuiTheme theme, String title, Setting setting, Collection> collection, RegistryKey> registryKey) { + super(theme, title, setting, collection, createUniverse(collection, registryKey)); + + this.registryKey = registryKey; + } + + private static Iterable> createUniverse(Collection> collection, RegistryKey> registryKey) { + Set> set = new ReferenceOpenHashSet<>(collection); + + Optional.ofNullable(MinecraftClient.getInstance().getNetworkHandler()) + .map(networkHandler -> (RegistryWrapper.WrapperLookup) networkHandler.getRegistryManager()) + .orElseGet(BuiltinRegistries::createWrapperLookup) + .getOptional(registryKey) + .ifPresent(registry -> registry.streamKeys().forEach(set::add)); + + return set; + } + + @Override + protected void postWidgets(WTable left, WTable right) { + if (!left.cells.isEmpty()) { + left.add(theme.horizontalSeparator()).expandX(); + left.row(); + } + + WHorizontalList manualEntry = left.add(theme.horizontalList()).expandX().widget(); + WTextBox textBox = manualEntry.add(theme.textBox("minecraft:")).expandX().minWidth(120d).widget(); + manualEntry.add(theme.plus()).expandCellX().right().widget().action = () -> { + String entry = textBox.get().trim(); + try { + Identifier id = entry.contains(":") ? Identifier.of(entry) : Identifier.ofVanilla(entry); + addValue(RegistryKey.of(registryKey, id)); + } catch (InvalidIdentifierException ignored) {} + }; + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/SortingHelper.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/SortingHelper.java new file mode 100644 index 0000000000..ed0b9be450 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/settings/base/SortingHelper.java @@ -0,0 +1,144 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.gui.screens.settings.base; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import meteordevelopment.meteorclient.utils.Utils; +import net.minecraft.util.collection.IndexedIterable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public final class SortingHelper { + private static final Comparator> FILTER_COMPARATOR = Comparator.comparingInt(Entry::distance); + + private SortingHelper() {} + + public static Iterable sort(Iterable registry, Predicate filter, Function nameFunction, String filterText) { + return sortInternal(registry, filter, nameFunction, filterText, null); + } + + public static Iterable sortWithPriority(Iterable registry, Predicate filter, Function nameFunction, String filterText, Comparator comparator) { + return sortInternal(registry, filter, nameFunction, filterText, comparator); + } + + private static Iterable sortInternal(Iterable registry, Predicate filter, Function nameFunction, String filterText, @Nullable Comparator comparator) { + if (filterText.isBlank()) { + if (comparator == null) { + return filtering(registry, filter); + } else { + List list = createList(registry); + + for (T value : registry) { + if (filter.test(value)) { + list.add(value); + } + } + + list.sort(comparator); + + return list; + } + } else { + List> list = createList(registry); + + for (T value : registry) { + if (!filter.test(value)) { + continue; + } + + String[] names = nameFunction.apply(value); + int bestWords = 0; + int bestDistance = Integer.MAX_VALUE; + float relevancy = 0f; + for (String name : names) { + int words = Utils.searchInWords(name, filterText); + int distance = Utils.searchLevenshteinDefault(name, filterText, false); + bestWords = Math.max(bestWords, words); + bestDistance = Math.min(bestDistance, distance); + relevancy = Math.max(relevancy, 1f - (float) distance / name.length()); + } + + if (bestWords > 0 || relevancy >= 0.5f) { + list.add(new Entry<>(value, bestDistance)); + } + } + + Comparator> entryComparator = comparator != null + ? Comparator., T>comparing(Entry::value, comparator).thenComparing(filterComparator()) + : filterComparator(); + + list.sort(entryComparator); + + return iterate(list); + } + } + + private static List createList(Iterable iterable) { + if (iterable instanceof IndexedIterable indexed) { + return new ObjectArrayList<>(indexed.size()); + } else if (iterable instanceof Collection collection) { + return new ObjectArrayList<>(collection.size()); + } else { + return new ObjectArrayList<>(); + } + } + + @SuppressWarnings("unchecked") + private static Comparator> filterComparator() { + return (Comparator>) (Object) FILTER_COMPARATOR; + } + + private static Iterable iterate(List> sortedList) { + return new Iterable<>() { + @NotNull + @Override + public Iterator iterator() { + return new Iterator<>() { + private final Iterator> it = sortedList.iterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public T next() { + return it.next().value(); + } + }; + } + }; + } + + private static Iterable filtering(Iterable iterable, Predicate filter) { + return new Iterable<>() { + @NotNull + @Override + public Iterator iterator() { + throw new UnsupportedOperationException("iterator() not supported by this Iterable, use forEach() instead."); + } + + @Override + public void forEach(Consumer action) { + for (T value : iterable) { + if (filter.test(value)) { + action.accept(value); + } + } + } + }; + } + + public record Entry(T value, int distance) {} +} diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/IdentifierAccessor.java b/src/main/java/meteordevelopment/meteorclient/mixin/IdentifierAccessor.java deleted file mode 100644 index 9c91897b9c..0000000000 --- a/src/main/java/meteordevelopment/meteorclient/mixin/IdentifierAccessor.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). - * Copyright (c) Meteor Development. - */ - -package meteordevelopment.meteorclient.mixin; - -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(Identifier.class) -public interface IdentifierAccessor { - @Mutable - @Accessor - void setPath(String path); -} diff --git a/src/main/resources/meteor-client.mixins.json b/src/main/resources/meteor-client.mixins.json index f124d1e770..891af51395 100644 --- a/src/main/resources/meteor-client.mixins.json +++ b/src/main/resources/meteor-client.mixins.json @@ -102,7 +102,6 @@ "HeadFeatureRendererMixin", "HeldItemRendererMixin", "HorseScreenHandlerAccessor", - "IdentifierAccessor", "InGameHudMixin", "InGameOverlayRendererMixin", "ItemEntityRendererMixin",