瀏覽代碼

Merge remote-tracking branch 'magneticflux-/object-list-list-entry' into v2

shedaniel 5 年之前
父節點
當前提交
d36dc91b3c
共有 41 個文件被更改,包括 1431 次插入1152 次删除
  1. 24 8
      build.gradle
  2. 7 7
      gradle.properties
  3. 1 1
      gradle/wrapper/gradle-wrapper.properties
  4. 55 40
      src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java
  5. 4 3
      src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java
  6. 85 0
      src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractListListEntry.java
  7. 115 0
      src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractTextFieldListListEntry.java
  8. 90 56
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListEntry.java
  9. 58 109
      src/main/java/me/shedaniel/clothconfig2/gui/entries/DoubleListListEntry.java
  10. 161 149
      src/main/java/me/shedaniel/clothconfig2/gui/entries/DropdownBoxEntry.java
  11. 56 109
      src/main/java/me/shedaniel/clothconfig2/gui/entries/FloatListListEntry.java
  12. 57 109
      src/main/java/me/shedaniel/clothconfig2/gui/entries/IntegerListListEntry.java
  13. 47 110
      src/main/java/me/shedaniel/clothconfig2/gui/entries/LongListListEntry.java
  14. 152 0
      src/main/java/me/shedaniel/clothconfig2/gui/entries/MultiElementListEntry.java
  15. 109 0
      src/main/java/me/shedaniel/clothconfig2/gui/entries/NestedListListEntry.java
  16. 2 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/SelectionListEntry.java
  17. 39 81
      src/main/java/me/shedaniel/clothconfig2/gui/entries/StringListListEntry.java
  18. 11 11
      src/main/java/me/shedaniel/clothconfig2/gui/entries/SubCategoryListEntry.java
  19. 7 6
      src/main/java/me/shedaniel/clothconfig2/gui/entries/TooltipListEntry.java
  20. 18 20
      src/main/java/me/shedaniel/clothconfig2/impl/ScissorsHandlerImpl.java
  21. 27 23
      src/main/java/me/shedaniel/clothconfig2/impl/builders/BooleanToggleBuilder.java
  22. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleFieldBuilder.java
  23. 34 43
      src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleListBuilder.java
  24. 6 6
      src/main/java/me/shedaniel/clothconfig2/impl/builders/DropdownMenuBuilder.java
  25. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/EnumSelectorBuilder.java
  26. 24 19
      src/main/java/me/shedaniel/clothconfig2/impl/builders/FieldBuilder.java
  27. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatFieldBuilder.java
  28. 34 43
      src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatListBuilder.java
  29. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/IntFieldBuilder.java
  30. 34 43
      src/main/java/me/shedaniel/clothconfig2/impl/builders/IntListBuilder.java
  31. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/IntSliderBuilder.java
  32. 22 19
      src/main/java/me/shedaniel/clothconfig2/impl/builders/KeyCodeBuilder.java
  33. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/LongFieldBuilder.java
  34. 34 43
      src/main/java/me/shedaniel/clothconfig2/impl/builders/LongListBuilder.java
  35. 7 5
      src/main/java/me/shedaniel/clothconfig2/impl/builders/LongSliderBuilder.java
  36. 6 4
      src/main/java/me/shedaniel/clothconfig2/impl/builders/SelectorBuilder.java
  37. 7 5
      src/main/java/me/shedaniel/clothconfig2/impl/builders/StringFieldBuilder.java
  38. 30 29
      src/main/java/me/shedaniel/clothconfig2/impl/builders/StringListBuilder.java
  39. 12 10
      src/main/java/me/shedaniel/clothconfig2/impl/builders/SubCategoryBuilder.java
  40. 13 10
      src/main/java/me/shedaniel/clothconfig2/impl/builders/TextDescriptionBuilder.java
  41. 7 5
      src/main/java/me/shedaniel/clothconfig2/impl/builders/TextFieldBuilder.java

+ 24 - 8
build.gradle

@@ -1,7 +1,7 @@
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
 
 
 plugins {
 plugins {
-    id 'fabric-loom' version '0.2.5-SNAPSHOT'
+    id 'fabric-loom' version '0.2.6-SNAPSHOT'
     id 'maven-publish'
     id 'maven-publish'
     id 'maven'
     id 'maven'
     id 'signing'
     id 'signing'
@@ -10,12 +10,21 @@ plugins {
     id 'java-library'
     id 'java-library'
 }
 }
 
 
+repositories {
+    maven {
+        name = 'Fabric'
+        url = 'https://maven.fabricmc.net/'
+    }
+    jcenter()
+    maven { url "https://jitpack.io" }
+}
+
 sourceCompatibility = 1.8
 sourceCompatibility = 1.8
 targetCompatibility = 1.8
 targetCompatibility = 1.8
 
 
 group = "me.shedaniel.cloth"
 group = "me.shedaniel.cloth"
 archivesBaseName = "config-2"
 archivesBaseName = "config-2"
-version = ((String) project.mod_version).contains("unstable") ? (project.mod_version + "." + buildTime()) : project.mod_version
+version = (project.mod_version as String).contains("unstable") ? (project.mod_version + "." + buildTime()) : project.mod_version as String
 
 
 minecraft {
 minecraft {
 }
 }
@@ -34,16 +43,22 @@ static def buildTime() {
 }
 }
 
 
 dependencies {
 dependencies {
+    compileOnly 'org.jetbrains:annotations:18.0.0'
+
     minecraft "com.mojang:minecraft:${project.minecraft_version}"
     minecraft "com.mojang:minecraft:${project.minecraft_version}"
-    mappings "net.fabricmc:yarn:${project.yarn_version}"
-    modApi "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
+    mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
+
+    modApi "net.fabricmc:fabric-loader:${project.loader_version}"
     modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
     modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
+
     modApi "me.shedaniel.cloth:basic-math:0.3.0"
     modApi "me.shedaniel.cloth:basic-math:0.3.0"
     include "me.shedaniel.cloth:basic-math:0.3.0"
     include "me.shedaniel.cloth:basic-math:0.3.0"
+
     modImplementation "io.github.prospector:modmenu:${modmenu_version}"
     modImplementation "io.github.prospector:modmenu:${modmenu_version}"
-    compileOnly "com.google.code.findbugs:jsr305:3.0.2"
-    //https://github.com/natanfudge/Not-Enough-Crashes/blob/master/README.md
-    modCompileOnly "com.lettuce.fudge:notenoughcrashes:$nec_version"
+
+    modImplementation "com.lettuce.fudge:notenoughcrashes-api:1.0.0"
+    include "com.lettuce.fudge:notenoughcrashes-api:1.0.0"
+    modRuntime "com.lettuce.fudge:notenoughcrashes:$nec_version"
 }
 }
 
 
 bintray {
 bintray {
@@ -148,7 +163,8 @@ curseforge {
         project {
         project {
             id = '319057'
             id = '319057'
             releaseType = 'release'
             releaseType = 'release'
-            addGameVersion '1.15-Snapshot'
+            addGameVersion '1.15.1'
+            addGameVersion 'Fabric'
             addGameVersion 'Java 8'
             addGameVersion 'Java 8'
             relations {
             relations {
                 requiredDependency 'fabric-api'
                 requiredDependency 'fabric-api'

+ 7 - 7
gradle.properties

@@ -1,7 +1,7 @@
-minecraft_version=1.15
-yarn_version=1.15+build.1
-fabric_loader_version=0.7.2+build.174
-fabric_version=0.4.20+build.273-1.15
-mod_version=2.8.3
-modmenu_version=1.7.14-unstable.19w42a+build.10
-nec_version=1.1.5+1.15.1
+minecraft_version=1.15.1
+yarn_mappings=1.15.1+build.37
+loader_version=0.7.4+build.177
+fabric_version=0.4.28+build.288-1.15
+mod_version=2.8.4
+modmenu_version=1.8.4+build.20
+nec_version=1.2.3+1.15.1

+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip

+ 55 - 40
src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java

@@ -36,15 +36,14 @@ import java.nio.file.Files;
 import java.util.*;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
-@SuppressWarnings("unchecked")
 public class ClothConfigInitializer implements ClientModInitializer {
 public class ClothConfigInitializer implements ClientModInitializer {
-    
+
     public static final Logger LOGGER = LogManager.getFormatterLogger("ClothConfig");
     public static final Logger LOGGER = LogManager.getFormatterLogger("ClothConfig");
     private static EasingMethod easingMethod = EasingMethodImpl.LINEAR;
     private static EasingMethod easingMethod = EasingMethodImpl.LINEAR;
     private static long scrollDuration = 600;
     private static long scrollDuration = 600;
     private static double scrollStep = 19;
     private static double scrollStep = 19;
     private static double bounceBackMultiplier = .24;
     private static double bounceBackMultiplier = .24;
-    
+
     public static double handleScrollingPosition(double[] target, double scroll, double maxScroll, float delta, double start, double duration) {
     public static double handleScrollingPosition(double[] target, double scroll, double maxScroll, float delta, double start, double duration) {
         if (getBounceBackMultiplier() >= 0) {
         if (getBounceBackMultiplier() >= 0) {
             target[0] = clamp(target[0], maxScroll);
             target[0] = clamp(target[0], maxScroll);
@@ -60,51 +59,39 @@ public class ClothConfigInitializer implements ClientModInitializer {
         else
         else
             return target[0];
             return target[0];
     }
     }
-    
+
     public static double expoEase(double start, double end, double amount) {
     public static double expoEase(double start, double end, double amount) {
         return start + (end - start) * getEasingMethod().apply(amount);
         return start + (end - start) * getEasingMethod().apply(amount);
     }
     }
-    
-    public static class Precision {
-        public static final float FLOAT_EPSILON = 1e-3f;
-        public static final double DOUBLE_EPSILON = 1e-7;
-        
-        public static boolean almostEquals(float value1, float value2, float acceptableDifference) {
-            return Math.abs(value1 - value2) <= acceptableDifference;
-        }
-        
-        public static boolean almostEquals(double value1, double value2, double acceptableDifference) {
-            return Math.abs(value1 - value2) <= acceptableDifference;
-        }
-    }
-    
+
     public static double clamp(double v, double maxScroll) {
     public static double clamp(double v, double maxScroll) {
         return clamp(v, maxScroll, DynamicEntryListWidget.SmoothScrollingSettings.CLAMP_EXTENSION);
         return clamp(v, maxScroll, DynamicEntryListWidget.SmoothScrollingSettings.CLAMP_EXTENSION);
     }
     }
-    
+
     public static double clamp(double v, double maxScroll, double clampExtension) {
     public static double clamp(double v, double maxScroll, double clampExtension) {
         return MathHelper.clamp(v, -clampExtension, maxScroll + clampExtension);
         return MathHelper.clamp(v, -clampExtension, maxScroll + clampExtension);
     }
     }
-    
+
     public static EasingMethod getEasingMethod() {
     public static EasingMethod getEasingMethod() {
         return easingMethod;
         return easingMethod;
     }
     }
-    
+
     public static long getScrollDuration() {
     public static long getScrollDuration() {
         return scrollDuration;
         return scrollDuration;
     }
     }
-    
+
     public static double getScrollStep() {
     public static double getScrollStep() {
         return scrollStep;
         return scrollStep;
     }
     }
-    
+
     public static double getBounceBackMultiplier() {
     public static double getBounceBackMultiplier() {
         return bounceBackMultiplier;
         return bounceBackMultiplier;
     }
     }
-    
+
     private static void loadConfig() {
     private static void loadConfig() {
         File file = new File(FabricLoader.getInstance().getConfigDirectory(), "cloth-config2/config.properties");
         File file = new File(FabricLoader.getInstance().getConfigDirectory(), "cloth-config2/config.properties");
         try {
         try {
+            //noinspection ResultOfMethodCallIgnored
             file.getParentFile().mkdirs();
             file.getParentFile().mkdirs();
             easingMethod = EasingMethodImpl.LINEAR;
             easingMethod = EasingMethodImpl.LINEAR;
             scrollDuration = 600;
             scrollDuration = 600;
@@ -138,7 +125,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
         }
         }
         saveConfig();
         saveConfig();
     }
     }
-    
+
     private static void saveConfig() {
     private static void saveConfig() {
         File file = new File(FabricLoader.getInstance().getConfigDirectory(), "cloth-config2/config.properties");
         File file = new File(FabricLoader.getInstance().getConfigDirectory(), "cloth-config2/config.properties");
         try {
         try {
@@ -158,7 +145,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
             bounceBackMultiplier = .24;
             bounceBackMultiplier = .24;
         }
         }
     }
     }
-    
+
     @Override
     @Override
     public void onInitializeClient() {
     public void onInitializeClient() {
         loadConfig();
         loadConfig();
@@ -177,11 +164,11 @@ public class ClothConfigInitializer implements ClientModInitializer {
                                 if (m.toString().equals(str))
                                 if (m.toString().equals(str))
                                     return m;
                                     return m;
                             return null;
                             return null;
-                        })).setDefaultValue(EasingMethodImpl.LINEAR).setSaveConsumer(o -> easingMethod = (EasingMethod) o).setSelections(EasingMethods.getMethods()).build();
+                        })).setDefaultValue(EasingMethodImpl.LINEAR).setSaveConsumer(o -> easingMethod = o).setSelections(EasingMethods.getMethods()).build();
                         LongSliderEntry scrollDurationEntry = entryBuilder.startLongSlider("option.cloth-config.scrollDuration", scrollDuration, 0, 5000).setTextGetter(integer -> integer <= 0 ? "Value: Disabled" : (integer > 1500 ? String.format("Value: %.1fs", integer / 1000f) : "Value: " + integer + "ms")).setDefaultValue(600).setSaveConsumer(i -> scrollDuration = i).build();
                         LongSliderEntry scrollDurationEntry = entryBuilder.startLongSlider("option.cloth-config.scrollDuration", scrollDuration, 0, 5000).setTextGetter(integer -> integer <= 0 ? "Value: Disabled" : (integer > 1500 ? String.format("Value: %.1fs", integer / 1000f) : "Value: " + integer + "ms")).setDefaultValue(600).setSaveConsumer(i -> scrollDuration = i).build();
                         DoubleListEntry scrollStepEntry = entryBuilder.startDoubleField("option.cloth-config.scrollStep", scrollStep).setDefaultValue(19).setSaveConsumer(i -> scrollStep = i).build();
                         DoubleListEntry scrollStepEntry = entryBuilder.startDoubleField("option.cloth-config.scrollStep", scrollStep).setDefaultValue(19).setSaveConsumer(i -> scrollStep = i).build();
                         LongSliderEntry bounceMultiplierEntry = entryBuilder.startLongSlider("option.cloth-config.bounceBackMultiplier", (long) (bounceBackMultiplier * 1000), -10, 750).setTextGetter(integer -> integer < 0 ? "Value: Disabled" : String.format("Value: %s", integer / 1000d)).setDefaultValue(240).setSaveConsumer(i -> bounceBackMultiplier = i / 1000d).build();
                         LongSliderEntry bounceMultiplierEntry = entryBuilder.startLongSlider("option.cloth-config.bounceBackMultiplier", (long) (bounceBackMultiplier * 1000), -10, 750).setTextGetter(integer -> integer < 0 ? "Value: Disabled" : String.format("Value: %s", integer / 1000d)).setDefaultValue(240).setSaveConsumer(i -> bounceBackMultiplier = i / 1000d).build();
-                        
+
                         scrolling.addEntry(new TooltipListEntry<Object>(I18n.translate("option.cloth-config.setDefaultSmoothScroll"), null) {
                         scrolling.addEntry(new TooltipListEntry<Object>(I18n.translate("option.cloth-config.setDefaultSmoothScroll"), null) {
                             int width = 220;
                             int width = 220;
                             private AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, getFieldName()) {
                             private AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, getFieldName()) {
@@ -195,26 +182,26 @@ public class ClothConfigInitializer implements ClientModInitializer {
                                 }
                                 }
                             };
                             };
                             private List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
                             private List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
-                            
+
                             @Override
                             @Override
                             public Object getValue() {
                             public Object getValue() {
                                 return null;
                                 return null;
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public Optional<Object> getDefaultValue() {
                             public Optional<Object> getDefaultValue() {
                                 return Optional.empty();
                                 return Optional.empty();
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public void save() {
                             public void save() {
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public List<? extends Element> children() {
                             public List<? extends Element> children() {
                                 return children;
                                 return children;
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
                             public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
                                 super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
                                 super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
@@ -226,7 +213,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
                                 this.buttonWidget.render(mouseX, mouseY, delta);
                                 this.buttonWidget.render(mouseX, mouseY, delta);
                             }
                             }
                         });
                         });
-                        
+
                         scrolling.addEntry(new TooltipListEntry<Object>(I18n.translate("option.cloth-config.disableSmoothScroll"), null) {
                         scrolling.addEntry(new TooltipListEntry<Object>(I18n.translate("option.cloth-config.disableSmoothScroll"), null) {
                             int width = 220;
                             int width = 220;
                             private AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, getFieldName()) {
                             private AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, getFieldName()) {
@@ -240,26 +227,26 @@ public class ClothConfigInitializer implements ClientModInitializer {
                                 }
                                 }
                             };
                             };
                             private List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
                             private List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
-                            
+
                             @Override
                             @Override
                             public Object getValue() {
                             public Object getValue() {
                                 return null;
                                 return null;
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public Optional<Object> getDefaultValue() {
                             public Optional<Object> getDefaultValue() {
                                 return Optional.empty();
                                 return Optional.empty();
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public void save() {
                             public void save() {
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public List<? extends Element> children() {
                             public List<? extends Element> children() {
                                 return children;
                                 return children;
                             }
                             }
-                            
+
                             @Override
                             @Override
                             public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
                             public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
                                 super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
                                 super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
@@ -279,6 +266,21 @@ public class ClothConfigInitializer implements ClientModInitializer {
                         testing.addEntry(entryBuilder.startDropdownMenu("lol apple", DropdownMenuBuilder.TopCellElementBuilder.ofItemObject(Items.APPLE), DropdownMenuBuilder.CellCreatorBuilder.ofItemObject()).setDefaultValue(Items.APPLE).setSelections(Registry.ITEM.stream().sorted(Comparator.comparing(Item::toString)).collect(Collectors.toCollection(LinkedHashSet::new))).setSaveConsumer(item -> System.out.println("save this " + item)).build());
                         testing.addEntry(entryBuilder.startDropdownMenu("lol apple", DropdownMenuBuilder.TopCellElementBuilder.ofItemObject(Items.APPLE), DropdownMenuBuilder.CellCreatorBuilder.ofItemObject()).setDefaultValue(Items.APPLE).setSelections(Registry.ITEM.stream().sorted(Comparator.comparing(Item::toString)).collect(Collectors.toCollection(LinkedHashSet::new))).setSaveConsumer(item -> System.out.println("save this " + item)).build());
                         testing.addEntry(entryBuilder.startKeyCodeField("Cool Key", InputUtil.UNKNOWN_KEYCODE).setDefaultValue(InputUtil.UNKNOWN_KEYCODE).build());
                         testing.addEntry(entryBuilder.startKeyCodeField("Cool Key", InputUtil.UNKNOWN_KEYCODE).setDefaultValue(InputUtil.UNKNOWN_KEYCODE).build());
                         testing.addEntry(entryBuilder.startModifierKeyCodeField("Cool Modifier Key", ModifierKeyCode.of(InputUtil.Type.KEYSYM.createFromCode(79), Modifier.of(false, true, false))).setDefaultValue(ModifierKeyCode.of(InputUtil.Type.KEYSYM.createFromCode(79), Modifier.of(false, true, false))).build());
                         testing.addEntry(entryBuilder.startModifierKeyCodeField("Cool Modifier Key", ModifierKeyCode.of(InputUtil.Type.KEYSYM.createFromCode(79), Modifier.of(false, true, false))).setDefaultValue(ModifierKeyCode.of(InputUtil.Type.KEYSYM.createFromCode(79), Modifier.of(false, true, false))).build());
+                        testing.addEntry(
+                                entryBuilder.startDoubleList("A list of Doubles", Arrays.asList(1d, 2d, 3d))
+                                        .setDefaultValue(Arrays.asList(1d, 2d, 3d))
+                                        .build()
+                        );
+                        testing.addEntry(
+                                entryBuilder.startLongList("A list of Longs", Arrays.asList(1L, 2L, 3L))
+                                        .setDefaultValue(Arrays.asList(1L, 2L, 3L))
+                                        .build()
+                        );
+                        testing.addEntry(
+                                entryBuilder.startStrList("A list of Strings", Arrays.asList("abc", "xyz"))
+                                        .setDefaultValue(Arrays.asList("abc", "xyz"))
+                                        .build()
+                        );
                         builder.setSavingRunnable(ClothConfigInitializer::saveConfig);
                         builder.setSavingRunnable(ClothConfigInitializer::saveConfig);
                         builder.transparentBackground();
                         builder.transparentBackground();
                         MinecraftClient.getInstance().openScreen(builder.build());
                         MinecraftClient.getInstance().openScreen(builder.build());
@@ -295,5 +297,18 @@ public class ClothConfigInitializer implements ClientModInitializer {
             //            });
             //            });
         }
         }
     }
     }
-    
+
+    public static class Precision {
+        public static final float FLOAT_EPSILON = 1e-3f;
+        public static final double DOUBLE_EPSILON = 1e-7;
+
+        public static boolean almostEquals(float value1, float value2, float acceptableDifference) {
+            return Math.abs(value1 - value2) <= acceptableDifference;
+        }
+
+        public static boolean almostEquals(double value1, double value2, double acceptableDifference) {
+            return Math.abs(value1 - value2) <= acceptableDifference;
+        }
+    }
+
 }
 }

+ 4 - 3
src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java

@@ -28,8 +28,8 @@ import net.minecraft.util.Identifier;
 import net.minecraft.util.Pair;
 import net.minecraft.util.Pair;
 import net.minecraft.util.Tickable;
 import net.minecraft.util.Tickable;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.MathHelper;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nullable;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -65,9 +65,10 @@ public abstract class ClothConfigScreen extends Screen {
     private Map<String, Identifier> categoryBackgroundLocation;
     private Map<String, Identifier> categoryBackgroundLocation;
     private boolean transparentBackground = false;
     private boolean transparentBackground = false;
     private boolean editable = true;
     private boolean editable = true;
-    @Nullable private String defaultFallbackCategory = null;
+    @Nullable
+    private String defaultFallbackCategory = null;
     private boolean alwaysShowTabs = false;
     private boolean alwaysShowTabs = false;
-    
+
     @Deprecated
     @Deprecated
     public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o, boolean confirmSave, boolean displayErrors, boolean smoothScrollingList, Identifier defaultBackgroundLocation, Map<String, Identifier> categoryBackgroundLocation) {
     public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o, boolean confirmSave, boolean displayErrors, boolean smoothScrollingList, Identifier defaultBackgroundLocation, Map<String, Identifier> categoryBackgroundLocation) {
         super(new LiteralText(""));
         super(new LiteralText(""));

+ 85 - 0
src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractListListEntry.java

@@ -0,0 +1,85 @@
+package me.shedaniel.clothconfig2.gui.entries;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * @param <T>    the configuration object type
+ * @param <C>    the cell type
+ * @param <SELF> the "curiously recurring template pattern" type parameter
+ * @see BaseListEntry
+ */
+@ApiStatus.Internal
+public abstract class AbstractListListEntry<T, C extends AbstractListListEntry.AbstractListCell<T, C, SELF>, SELF extends AbstractListListEntry<T, C, SELF>>
+        extends BaseListEntry<T, C, SELF> {
+
+    protected final BiFunction<T, SELF, C> createNewCell;
+    protected Function<T, Optional<String>> cellErrorSupplier;
+
+    public AbstractListListEntry(
+            String fieldName,
+            List<T> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<T>> saveConsumer,
+            Supplier<List<T>> defaultValue,
+            String resetButtonKey,
+            boolean requiresRestart,
+            boolean deleteButtonEnabled,
+            boolean insertInFront,
+            BiFunction<T, SELF, C> createNewCell) {
+        super(fieldName, tooltipSupplier, defaultValue, abstractListListEntry -> createNewCell.apply(null, abstractListListEntry), saveConsumer, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront);
+        this.createNewCell = createNewCell;
+        for (T f : value)
+            cells.add(createNewCell.apply(f, this.self()));
+        this.widgets.addAll(cells);
+        expanded = defaultExpanded;
+    }
+
+    public Function<T, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+
+    public void setCellErrorSupplier(Function<T, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+
+    @Override
+    public List<T> getValue() {
+        return cells.stream().map(C::getValue).collect(Collectors.toList());
+    }
+
+    @Override
+    protected C getFromValue(T value) {
+        return createNewCell.apply(value, this.self());
+    }
+
+    /**
+     * @param <T>           the configuration object type
+     * @param <SELF>        the "curiously recurring template pattern" type parameter for this class
+     * @param <OUTER_SELF>> the "curiously recurring template pattern" type parameter for the outer class
+     * @see AbstractListListEntry
+     */
+    @ApiStatus.Internal
+    public static abstract class AbstractListCell<T, SELF extends AbstractListCell<T, SELF, OUTER_SELF>, OUTER_SELF extends AbstractListListEntry<T, SELF, OUTER_SELF>>
+            extends BaseListCell {
+        protected final OUTER_SELF listListEntry;
+
+        public AbstractListCell(@Nullable T value, OUTER_SELF listListEntry) {
+            this.listListEntry = listListEntry;
+            this.setErrorSupplier(() -> Optional.ofNullable(listListEntry.cellErrorSupplier).flatMap(cellErrorFn -> cellErrorFn.apply(this.getValue())));
+        }
+
+        public abstract T getValue();
+
+    }
+
+}

+ 115 - 0
src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractTextFieldListListEntry.java

@@ -0,0 +1,115 @@
+package me.shedaniel.clothconfig2.gui.entries;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.widget.TextFieldWidget;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * This class represents config entry lists that use one {@link TextFieldWidget} per entry.
+ *
+ * @param <T>    the configuration object type
+ * @param <C>    the cell type
+ * @param <SELF> the "curiously recurring template pattern" type parameter
+ * @see AbstractListListEntry
+ */
+@ApiStatus.Internal
+public abstract class AbstractTextFieldListListEntry<T, C extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<T, C, SELF>, SELF extends AbstractTextFieldListListEntry<T, C, SELF>>
+        extends AbstractListListEntry<T, C, SELF> {
+
+    public AbstractTextFieldListListEntry(
+            String fieldName,
+            List<T> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<T>> saveConsumer,
+            Supplier<List<T>> defaultValue,
+            String resetButtonKey,
+            boolean requiresRestart,
+            boolean deleteButtonEnabled,
+            boolean insertInFront,
+            BiFunction<T, SELF, C> createNewCell
+    ) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, createNewCell);
+    }
+
+    /**
+     * @param <T>           the configuration object type
+     * @param <SELF>        the "curiously recurring template pattern" type parameter for this class
+     * @param <OUTER_SELF>> the "curiously recurring template pattern" type parameter for the outer class
+     * @see AbstractTextFieldListListEntry
+     */
+    @ApiStatus.Internal
+    public static abstract class AbstractTextFieldListCell<T, SELF extends AbstractTextFieldListCell<T, SELF, OUTER_SELF>, OUTER_SELF extends AbstractTextFieldListListEntry<T, SELF, OUTER_SELF>>
+            extends AbstractListListEntry.AbstractListCell<T, SELF, OUTER_SELF> {
+
+        protected TextFieldWidget widget;
+
+        public AbstractTextFieldListCell(@Nullable T value, OUTER_SELF listListEntry) {
+            super(value, listListEntry);
+
+            final T finalValue = substituteDefault(value);
+
+            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "");
+            widget.setTextPredicate(this::isValidText);
+            widget.setMaxLength(Integer.MAX_VALUE);
+            widget.setHasBorder(false);
+            widget.setText(Objects.toString(finalValue));
+            widget.setChangedListener(s -> {
+                widget.setEditableColor(getPreferredTextColor());
+                if (!Objects.equals(s, Objects.toString(finalValue))) {
+                    this.listListEntry.getScreen().setEdited(true, this.listListEntry.isRequiresRestart());
+                }
+            });
+        }
+
+        /**
+         * Allows subclasses to substitute default values.
+         *
+         * @param value the (possibly null) value to substitute
+         * @return a substitution
+         */
+        @Nullable
+        protected abstract T substituteDefault(@Nullable T value);
+
+        /**
+         * Tests if the text entered is valid. If not, the text is not changed.
+         *
+         * @param text the text to test
+         * @return {@code true} if the text may be changed, {@code false} to prevent the change
+         */
+        protected abstract boolean isValidText(@NotNull String text);
+
+        @Override
+        public int getCellHeight() {
+            return 20;
+        }
+
+        @Override
+        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
+            widget.setWidth(entryWidth - 12);
+            widget.x = x;
+            widget.y = y + 1;
+            widget.setEditable(listListEntry.isEditable());
+            widget.render(mouseX, mouseY, delta);
+            if (isSelected && listListEntry.isEditable())
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+        }
+
+        @Override
+        public List<? extends Element> children() {
+            return Collections.singletonList(widget);
+        }
+    }
+
+}

+ 90 - 56
src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListEntry.java

@@ -14,33 +14,59 @@ import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.sound.PositionedSoundInstance;
 import net.minecraft.client.sound.PositionedSoundInstance;
 import net.minecraft.sound.SoundEvents;
 import net.minecraft.sound.SoundEvents;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * @param <T>    the configuration object type
+ * @param <C>    the cell type
+ * @param <SELF> the "curiously recurring template pattern" type parameter
+ * @implNote See <a href="https://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable">Is there a way to refer to the current type with a type variable?</href> on Stack Overflow.
+ */
+@ApiStatus.Internal
+public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends BaseListEntry<T, C, SELF>> extends TooltipListEntry<List<T>> {
 
 
-public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipListEntry<List<T>> {
-    
     protected static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
     protected static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
+    @NotNull
     protected final List<C> cells;
     protected final List<C> cells;
+    @NotNull
     protected final List<Element> widgets;
     protected final List<Element> widgets;
-    protected boolean expended;
+    protected boolean expanded;
+    protected boolean deleteButtonEnabled;
+    protected boolean insertInFront;
+    @Nullable
     protected Consumer<List<T>> saveConsumer;
     protected Consumer<List<T>> saveConsumer;
     protected ListLabelWidget labelWidget;
     protected ListLabelWidget labelWidget;
     protected AbstractButtonWidget resetWidget;
     protected AbstractButtonWidget resetWidget;
-    protected Function<BaseListEntry, C> createNewInstance;
+    @NotNull
+    protected Function<SELF, C> createNewInstance;
+    @NotNull
     protected Supplier<List<T>> defaultValue;
     protected Supplier<List<T>> defaultValue;
+    @Nullable
     protected String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     protected String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
-    
+
     @Deprecated
     @Deprecated
-    public BaseListEntry(String fieldName, Supplier<Optional<String[]>> tooltipSupplier, Supplier<List<T>> defaultValue, Function<BaseListEntry, C> createNewInstance, Consumer<List<T>> saveConsumer, String resetButtonKey) {
+    public BaseListEntry(@NotNull String fieldName, @Nullable Supplier<Optional<String[]>> tooltipSupplier, @NotNull Supplier<List<T>> defaultValue, @NotNull Function<SELF, C> createNewInstance, @Nullable Consumer<List<T>> saveConsumer, String resetButtonKey) {
         this(fieldName, tooltipSupplier, defaultValue, createNewInstance, saveConsumer, resetButtonKey, false);
         this(fieldName, tooltipSupplier, defaultValue, createNewInstance, saveConsumer, resetButtonKey, false);
     }
     }
-    
-    public BaseListEntry(String fieldName, Supplier<Optional<String[]>> tooltipSupplier, Supplier<List<T>> defaultValue, Function<BaseListEntry, C> createNewInstance, Consumer<List<T>> saveConsumer, String resetButtonKey, boolean requiresRestart) {
+
+    @Deprecated
+    public BaseListEntry(@NotNull String fieldName, @Nullable Supplier<Optional<String[]>> tooltipSupplier, @NotNull Supplier<List<T>> defaultValue, @NotNull Function<SELF, C> createNewInstance, @Nullable Consumer<List<T>> saveConsumer, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, tooltipSupplier, defaultValue, createNewInstance, saveConsumer, resetButtonKey, requiresRestart, true, true);
+    }
+
+    public BaseListEntry(@NotNull String fieldName, @Nullable Supplier<Optional<String[]>> tooltipSupplier, @NotNull Supplier<List<T>> defaultValue, @NotNull Function<SELF, C> createNewInstance, @Nullable Consumer<List<T>> saveConsumer, String resetButtonKey, boolean requiresRestart, boolean deleteButtonEnabled, boolean insertInFront) {
         super(fieldName, tooltipSupplier, requiresRestart);
         super(fieldName, tooltipSupplier, requiresRestart);
+        this.deleteButtonEnabled = deleteButtonEnabled;
+        this.insertInFront = insertInFront;
         this.cells = Lists.newArrayList();
         this.cells = Lists.newArrayList();
         this.labelWidget = new ListLabelWidget();
         this.labelWidget = new ListLabelWidget();
         this.widgets = Lists.newArrayList(labelWidget);
         this.widgets = Lists.newArrayList(labelWidget);
@@ -56,76 +82,83 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
     }
     }
-    
+
+    public abstract SELF self();
+
     public boolean isDeleteButtonEnabled() {
     public boolean isDeleteButtonEnabled() {
-        return true;
+        return deleteButtonEnabled;
     }
     }
-    
+
     protected abstract C getFromValue(T value);
     protected abstract C getFromValue(T value);
-    
-    public Function<BaseListEntry, C> getCreateNewInstance() {
+
+    @NotNull
+    public Function<SELF, C> getCreateNewInstance() {
         return createNewInstance;
         return createNewInstance;
     }
     }
-    
-    public void setCreateNewInstance(Function<BaseListEntry, C> createNewInstance) {
+
+    public void setCreateNewInstance(@NotNull Function<SELF, C> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
     }
     }
-    
+
+    @Nullable
     public String getAddTooltip() {
     public String getAddTooltip() {
         return addTooltip;
         return addTooltip;
     }
     }
-    
-    public void setAddTooltip(String addTooltip) {
+
+    public void setAddTooltip(@Nullable String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
     }
     }
-    
+
+    @Nullable
     public String getRemoveTooltip() {
     public String getRemoveTooltip() {
         return removeTooltip;
         return removeTooltip;
     }
     }
-    
-    public void setRemoveTooltip(String removeTooltip) {
+
+    public void setRemoveTooltip(@Nullable String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
     }
     }
-    
+
     @Override
     @Override
     public Optional<List<T>> getDefaultValue() {
     public Optional<List<T>> getDefaultValue() {
         return Optional.ofNullable(defaultValue.get());
         return Optional.ofNullable(defaultValue.get());
     }
     }
-    
+
     @Override
     @Override
     public int getItemHeight() {
     public int getItemHeight() {
-        if (expended) {
+        if (expanded) {
             int i = 24;
             int i = 24;
-            for(BaseListCell entry : cells)
+            for (BaseListCell entry : cells)
                 i += entry.getCellHeight();
                 i += entry.getCellHeight();
             return i;
             return i;
         }
         }
         return 24;
         return 24;
     }
     }
-    
+
     @Override
     @Override
     public List<? extends Element> children() {
     public List<? extends Element> children() {
         return widgets;
         return widgets;
     }
     }
-    
+
     @Override
     @Override
     public Optional<String> getError() {
     public Optional<String> getError() {
-        String error = null;
-        for(BaseListCell entry : cells)
-            if (entry.getConfigError().isPresent()) {
-                if (error != null)
-                    return Optional.ofNullable(I18n.translate("text.cloth-config.multi_error"));
-                return Optional.ofNullable((String) entry.getConfigError().get());
-            }
-        return Optional.ofNullable(error);
+        List<String> errors = cells.stream()
+                .map(C::getConfigError)
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .collect(Collectors.toList());
+
+        if (errors.size() > 1)
+            return Optional.of(I18n.translate("text.cloth-config.multi_error"));
+        else
+            return errors.stream().findFirst();
     }
     }
-    
+
     @Override
     @Override
     public void save() {
     public void save() {
         if (saveConsumer != null)
         if (saveConsumer != null)
             saveConsumer.accept(getValue());
             saveConsumer.accept(getValue());
     }
     }
-    
+
     @Override
     @Override
     public boolean isMouseInside(int mouseX, int mouseY, int x, int y, int entryWidth, int entryHeight) {
     public boolean isMouseInside(int mouseX, int mouseY, int x, int y, int entryWidth, int entryHeight) {
         labelWidget.rectangle.x = x - 15;
         labelWidget.rectangle.x = x - 15;
@@ -134,15 +167,15 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
         labelWidget.rectangle.height = 24;
         labelWidget.rectangle.height = 24;
         return labelWidget.rectangle.contains(mouseX, mouseY) && getParent().isMouseOver(mouseX, mouseY) && !resetWidget.isMouseOver(mouseX, mouseY);
         return labelWidget.rectangle.contains(mouseX, mouseY) && getParent().isMouseOver(mouseX, mouseY) && !resetWidget.isMouseOver(mouseX, mouseY);
     }
     }
-    
+
     protected boolean isInsideCreateNew(double mouseX, double mouseY) {
     protected boolean isInsideCreateNew(double mouseX, double mouseY) {
         return mouseX >= labelWidget.rectangle.x + 12 && mouseY >= labelWidget.rectangle.y + 3 && mouseX <= labelWidget.rectangle.x + 12 + 11 && mouseY <= labelWidget.rectangle.y + 3 + 11;
         return mouseX >= labelWidget.rectangle.x + 12 && mouseY >= labelWidget.rectangle.y + 3 && mouseX <= labelWidget.rectangle.x + 12 + 11 && mouseY <= labelWidget.rectangle.y + 3 + 11;
     }
     }
-    
+
     protected boolean isInsideDelete(double mouseX, double mouseY) {
     protected boolean isInsideDelete(double mouseX, double mouseY) {
         return isDeleteButtonEnabled() && mouseX >= labelWidget.rectangle.x + 25 && mouseY >= labelWidget.rectangle.y + 3 && mouseX <= labelWidget.rectangle.x + 25 + 11 && mouseY <= labelWidget.rectangle.y + 3 + 11;
         return isDeleteButtonEnabled() && mouseX >= labelWidget.rectangle.x + 25 && mouseY >= labelWidget.rectangle.y + 3 && mouseX <= labelWidget.rectangle.x + 25 + 11 && mouseY <= labelWidget.rectangle.y + 3 + 11;
     }
     }
-    
+
     public Optional<String[]> getTooltip(int mouseX, int mouseY) {
     public Optional<String[]> getTooltip(int mouseX, int mouseY) {
         if (addTooltip != null && isInsideCreateNew(mouseX, mouseY))
         if (addTooltip != null && isInsideCreateNew(mouseX, mouseY))
             return Optional.of(new String[]{addTooltip});
             return Optional.of(new String[]{addTooltip});
@@ -152,7 +185,7 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
             return getTooltipSupplier().get();
             return getTooltipSupplier().get();
         return Optional.empty();
         return Optional.empty();
     }
     }
-    
+
     @Override
     @Override
     public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
     public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
         labelWidget.rectangle.x = x - 19;
         labelWidget.rectangle.x = x - 19;
@@ -167,10 +200,10 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
         MinecraftClient.getInstance().getTextureManager().bindTexture(CONFIG_TEX);
         MinecraftClient.getInstance().getTextureManager().bindTexture(CONFIG_TEX);
         DiffuseLighting.disable();
         DiffuseLighting.disable();
         RenderSystem.color4f(1, 1, 1, 1);
         RenderSystem.color4f(1, 1, 1, 1);
-        BaseListCell focused = !expended || getFocused() == null || !(getFocused() instanceof BaseListCell) ? null : (BaseListCell) getFocused();
+        BaseListCell focused = !expanded || getFocused() == null || !(getFocused() instanceof BaseListCell) ? null : (BaseListCell) getFocused();
         boolean insideCreateNew = isInsideCreateNew(mouseX, mouseY);
         boolean insideCreateNew = isInsideCreateNew(mouseX, mouseY);
         boolean insideDelete = isInsideDelete(mouseX, mouseY);
         boolean insideDelete = isInsideDelete(mouseX, mouseY);
-        blit(x - 15, y + 4, 24 + 9, (labelWidget.rectangle.contains(mouseX, mouseY) && !insideCreateNew && !insideDelete ? 18 : 0) + (expended ? 9 : 0), 9, 9);
+        blit(x - 15, y + 4, 24 + 9, (labelWidget.rectangle.contains(mouseX, mouseY) && !insideCreateNew && !insideDelete ? 18 : 0) + (expanded ? 9 : 0), 9, 9);
         blit(x - 15 + 13, y + 4, 24 + 18, insideCreateNew ? 9 : 0, 9, 9);
         blit(x - 15 + 13, y + 4, 24 + 18, insideCreateNew ? 9 : 0, 9, 9);
         if (isDeleteButtonEnabled())
         if (isDeleteButtonEnabled())
             blit(x - 15 + 26, y + 4, 24 + 27, focused == null ? 0 : insideDelete ? 18 : 9, 9, 9);
             blit(x - 15 + 26, y + 4, 24 + 27, focused == null ? 0 : insideDelete ? 18 : 9, 9, 9);
@@ -179,42 +212,43 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
         resetWidget.active = isEditable() && getDefaultValue().isPresent();
         resetWidget.active = isEditable() && getDefaultValue().isPresent();
         resetWidget.render(mouseX, mouseY, delta);
         resetWidget.render(mouseX, mouseY, delta);
         MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), isDeleteButtonEnabled() ? x + 24 : x + 24 - 9, y + 5, labelWidget.rectangle.contains(mouseX, mouseY) && !resetWidget.isMouseOver(mouseX, mouseY) && !insideDelete && !insideCreateNew ? 0xffe6fe16 : getPreferredTextColor());
         MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), isDeleteButtonEnabled() ? x + 24 : x + 24 - 9, y + 5, labelWidget.rectangle.contains(mouseX, mouseY) && !resetWidget.isMouseOver(mouseX, mouseY) && !insideDelete && !insideCreateNew ? 0xffe6fe16 : getPreferredTextColor());
-        if (expended) {
+        if (expanded) {
             int yy = y + 24;
             int yy = y + 24;
-            for(BaseListCell cell : cells) {
+            for (BaseListCell cell : cells) {
                 cell.render(-1, yy, x + 14, entryWidth - 14, cell.getCellHeight(), mouseX, mouseY, getParent().getFocused() != null && getParent().getFocused().equals(this) && getFocused() != null && getFocused().equals(cell), delta);
                 cell.render(-1, yy, x + 14, entryWidth - 14, cell.getCellHeight(), mouseX, mouseY, getParent().getFocused() != null && getParent().getFocused().equals(this) && getFocused() != null && getFocused().equals(cell), delta);
                 yy += cell.getCellHeight();
                 yy += cell.getCellHeight();
             }
             }
         }
         }
     }
     }
-    
+
     public boolean insertInFront() {
     public boolean insertInFront() {
-        return true;
+        return insertInFront;
     }
     }
-    
+
     public class ListLabelWidget implements Element {
     public class ListLabelWidget implements Element {
         protected Rectangle rectangle = new Rectangle();
         protected Rectangle rectangle = new Rectangle();
-        
+
         @Override
         @Override
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
             if (resetWidget.isMouseOver(double_1, double_2)) {
             if (resetWidget.isMouseOver(double_1, double_2)) {
                 return false;
                 return false;
             } else if (isInsideCreateNew(double_1, double_2)) {
             } else if (isInsideCreateNew(double_1, double_2)) {
-                expended = true;
+                expanded = true;
                 C cell;
                 C cell;
                 if (insertInFront()) {
                 if (insertInFront()) {
-                    cells.add(0, cell = createNewInstance.apply(BaseListEntry.this));
+                    cells.add(0, cell = createNewInstance.apply(BaseListEntry.this.self()));
                     widgets.add(0, cell);
                     widgets.add(0, cell);
                 } else {
                 } else {
-                    cells.add(cell = createNewInstance.apply(BaseListEntry.this));
+                    cells.add(cell = createNewInstance.apply(BaseListEntry.this.self()));
                     widgets.add(cell);
                     widgets.add(cell);
                 }
                 }
                 getScreen().setEdited(true, isRequiresRestart());
                 getScreen().setEdited(true, isRequiresRestart());
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 return true;
                 return true;
             } else if (isDeleteButtonEnabled() && isInsideDelete(double_1, double_2)) {
             } else if (isDeleteButtonEnabled() && isInsideDelete(double_1, double_2)) {
-                BaseListCell focused = !expended && getFocused() == null || !(getFocused() instanceof BaseListCell) ? null : (BaseListCell) getFocused();
-                if (focused != null) {
+                Element focused = getFocused();
+                if (expanded && focused instanceof BaseListCell) {
+                    //noinspection SuspiciousMethodCalls
                     cells.remove(focused);
                     cells.remove(focused);
                     widgets.remove(focused);
                     widgets.remove(focused);
                     getScreen().setEdited(true, isRequiresRestart());
                     getScreen().setEdited(true, isRequiresRestart());
@@ -222,12 +256,12 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
                 }
                 }
                 return true;
                 return true;
             } else if (rectangle.contains(double_1, double_2)) {
             } else if (rectangle.contains(double_1, double_2)) {
-                expended = !expended;
+                expanded = !expanded;
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 return true;
                 return true;
             }
             }
             return false;
             return false;
         }
         }
     }
     }
-    
+
 }
 }

+ 58 - 109
src/main/java/me/shedaniel/clothconfig2/gui/entries/DoubleListListEntry.java

@@ -1,123 +1,94 @@
 package me.shedaniel.clothconfig2.gui.entries;
 package me.shedaniel.clothconfig2.gui.entries;
 
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.gui.widget.TextFieldWidget;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 
-public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEntry.DoubleListCell> {
-    
+@ApiStatus.Internal
+public class DoubleListListEntry extends AbstractTextFieldListListEntry<Double, DoubleListListEntry.DoubleListCell, DoubleListListEntry> {
+
     private double minimum, maximum;
     private double minimum, maximum;
-    private Function<Double, Optional<String>> cellErrorSupplier;
-    
+
     @Deprecated
     @Deprecated
-    public DoubleListListEntry(String fieldName, List<Double> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Double>> saveConsumer, Supplier<List<Double>> defaultValue, String resetButtonKey) {
-        this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
+    public DoubleListListEntry(String fieldName, List<Double> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Double>> saveConsumer, Supplier<List<Double>> defaultValue, String resetButtonKey) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
-    public DoubleListListEntry(String fieldName, List<Double> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Double>> saveConsumer, Supplier<List<Double>> defaultValue, String resetButtonKey, boolean requiresRestart) {
-        super(fieldName, tooltipSupplier, defaultValue, baseListEntry -> new DoubleListCell(0d, (DoubleListListEntry) baseListEntry), saveConsumer, resetButtonKey, requiresRestart);
-        this.minimum = -Double.MAX_VALUE;
-        this.maximum = Double.MAX_VALUE;
-        for(Double f : value)
-            cells.add(new DoubleListCell(f, this));
-        this.widgets.addAll(cells);
-        expended = defaultExpended;
-    }
-    
-    public Function<Double, Optional<String>> getCellErrorSupplier() {
-        return cellErrorSupplier;
+    public DoubleListListEntry(String fieldName, List<Double> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Double>> saveConsumer, Supplier<List<Double>> defaultValue, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, true, true);
     }
     }
-    
-    public void setCellErrorSupplier(Function<Double, Optional<String>> cellErrorSupplier) {
-        this.cellErrorSupplier = cellErrorSupplier;
-    }
-    
-    @Override
-    public List<Double> getValue() {
-        return cells.stream().map(DoubleListCell::getValue).collect(Collectors.toList());
+
+    public DoubleListListEntry(
+            String fieldName,
+            List<Double> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<Double>> saveConsumer,
+            Supplier<List<Double>> defaultValue,
+            String resetButtonKey,
+            boolean requiresRestart,
+            boolean deleteButtonEnabled,
+            boolean insertInFront
+    ) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, DoubleListCell::new);
+        this.minimum = Double.NEGATIVE_INFINITY;
+        this.maximum = Double.POSITIVE_INFINITY;
     }
     }
-    
+
     public DoubleListListEntry setMaximum(Double maximum) {
     public DoubleListListEntry setMaximum(Double maximum) {
         this.maximum = maximum;
         this.maximum = maximum;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListListEntry setMinimum(Double minimum) {
     public DoubleListListEntry setMinimum(Double minimum) {
         this.minimum = minimum;
         this.minimum = minimum;
         return this;
         return this;
     }
     }
-    
+
     @Override
     @Override
-    protected DoubleListCell getFromValue(Double value) {
-        return new DoubleListCell(value, this);
+    public DoubleListListEntry self() {
+        return this;
     }
     }
-    
-    public static class DoubleListCell extends BaseListCell {
-        
-        private Function<String, String> stripCharacters = s -> {
-            StringBuilder stringBuilder_1 = new StringBuilder();
-            char[] var2 = s.toCharArray();
-            int var3 = var2.length;
-            
-            for(int var4 = 0; var4 < var3; ++var4)
-                if (Character.isDigit(var2[var4]) || var2[var4] == '-' || var2[var4] == '.')
-                    stringBuilder_1.append(var2[var4]);
-            
-            return stringBuilder_1.toString();
-        };
-        private TextFieldWidget widget;
-        private boolean isSelected;
-        private DoubleListListEntry listListEntry;
-        
-        public DoubleListCell(double value, DoubleListListEntry listListEntry) {
-            this.listListEntry = listListEntry;
-            this.setErrorSupplier(() -> listListEntry.cellErrorSupplier == null ? Optional.empty() : listListEntry.getCellErrorSupplier().apply(getValue()));
-            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
-                @Override
-                public void render(int int_1, int int_2, float Double_1) {
-                    boolean f = isFocused();
-                    setFocused(isSelected);
-                    widget.setEditableColor(getPreferredTextColor());
-                    super.render(int_1, int_2, Double_1);
-                    setFocused(f);
-                }
-                
-                @Override
-                public void write(String string_1) {
-                    super.write(stripCharacters.apply(string_1));
-                }
-            };
-            widget.setMaxLength(999999);
-            widget.setHasBorder(false);
-            widget.setText(value + "");
-            widget.setChangedListener(s -> {
-                if (!(value + "").equalsIgnoreCase(s))
-                    listListEntry.getScreen().setEdited(true, listListEntry.isRequiresRestart());
-            });
+
+    public static class DoubleListCell extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<Double, DoubleListCell, DoubleListListEntry> {
+
+        public DoubleListCell(Double value, final DoubleListListEntry listListEntry) {
+            super(value, listListEntry);
+        }
+
+        @Nullable
+        @Override
+        protected Double substituteDefault(@Nullable Double value) {
+            if (value == null)
+                return 0d;
+            else
+                return value;
+        }
+
+        @Override
+        protected boolean isValidText(@NotNull String text) {
+            return text.chars().allMatch(c -> Character.isDigit(c) || c == '-' || c == '.');
         }
         }
-        
-        public double getValue() {
+
+        public Double getValue() {
             try {
             try {
                 return Double.valueOf(widget.getText());
                 return Double.valueOf(widget.getText());
             } catch (NumberFormatException e) {
             } catch (NumberFormatException e) {
                 return 0d;
                 return 0d;
             }
             }
         }
         }
-        
+
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
-                double i = Double.valueOf(widget.getText());
+                double i = Double.parseDouble(widget.getText());
                 if (i > listListEntry.maximum)
                 if (i > listListEntry.maximum)
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                 else if (i < listListEntry.minimum)
                 else if (i < listListEntry.minimum)
@@ -127,29 +98,7 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
             }
             }
             return Optional.empty();
             return Optional.empty();
         }
         }
-        
-        @Override
-        public int getCellHeight() {
-            return 20;
-        }
-        
-        @Override
-        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-            widget.setWidth(entryWidth - 12);
-            widget.x = x;
-            widget.y = y + 1;
-            widget.setEditable(listListEntry.isEditable());
-            this.isSelected = isSelected;
-            widget.render(mouseX, mouseY, delta);
-            if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
-        }
-        
-        @Override
-        public List<? extends Element> children() {
-            return Collections.singletonList(widget);
-        }
-        
+
     }
     }
-    
+
 }
 }

+ 161 - 149
src/main/java/me/shedaniel/clothconfig2/gui/entries/DropdownBoxEntry.java

@@ -23,9 +23,9 @@ import net.minecraft.client.render.VertexFormats;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.util.Window;
 import net.minecraft.client.util.Window;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.MathHelper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.util.*;
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Function;
@@ -33,20 +33,22 @@ import java.util.function.Supplier;
 
 
 @SuppressWarnings("deprecation")
 @SuppressWarnings("deprecation")
 public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
 public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
-    
+
     protected ButtonWidget resetButton;
     protected ButtonWidget resetButton;
     protected SelectionElement<T> selectionElement;
     protected SelectionElement<T> selectionElement;
-    @Nonnull private Supplier<T> defaultValue;
-    @Nullable private Consumer<T> saveConsumer;
-    
+    @NotNull
+    private Supplier<T> defaultValue;
+    @Nullable
+    private Consumer<T> saveConsumer;
+
     @Deprecated
     @Deprecated
     public DropdownBoxEntry(String fieldName,
     public DropdownBoxEntry(String fieldName,
-            @Nonnull String resetButtonKey,
-            @Nullable Supplier<Optional<String[]>> tooltipSupplier, boolean requiresRestart,
-            @Nullable Supplier<T> defaultValue,
-            @Nullable Consumer<T> saveConsumer,
-            @Nullable Iterable<T> selections,
-            @Nonnull SelectionTopCellElement<T> topRenderer, @Nonnull SelectionCellCreator<T> cellCreator) {
+                            @NotNull String resetButtonKey,
+                            @Nullable Supplier<Optional<String[]>> tooltipSupplier, boolean requiresRestart,
+                            @Nullable Supplier<T> defaultValue,
+                            @Nullable Consumer<T> saveConsumer,
+                            @Nullable Iterable<T> selections,
+                            @NotNull SelectionTopCellElement<T> topRenderer, @NotNull SelectionCellCreator<T> cellCreator) {
         super(I18n.translate(fieldName), tooltipSupplier, requiresRestart);
         super(I18n.translate(fieldName), tooltipSupplier, requiresRestart);
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
@@ -56,7 +58,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         });
         });
         this.selectionElement = new SelectionElement(this, new Rectangle(0, 0, 150, 20), new DefaultDropdownMenuElement(selections == null ? ImmutableList.of() : ImmutableList.copyOf(selections)), topRenderer, cellCreator);
         this.selectionElement = new SelectionElement(this, new Rectangle(0, 0, 150, 20), new DefaultDropdownMenuElement(selections == null ? ImmutableList.of() : ImmutableList.copyOf(selections)), topRenderer, cellCreator);
     }
     }
-    
+
     @Override
     @Override
     public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
     public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
         super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
         super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
@@ -78,58 +80,58 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         resetButton.render(mouseX, mouseY, delta);
         resetButton.render(mouseX, mouseY, delta);
         selectionElement.render(mouseX, mouseY, delta);
         selectionElement.render(mouseX, mouseY, delta);
     }
     }
-    
-    @Nonnull
+
+    @NotNull
     public ImmutableList<T> getSelections() {
     public ImmutableList<T> getSelections() {
         return selectionElement.menu.getSelections();
         return selectionElement.menu.getSelections();
     }
     }
-    
+
     @Override
     @Override
     public T getValue() {
     public T getValue() {
         return selectionElement.getValue();
         return selectionElement.getValue();
     }
     }
-    
+
     @Deprecated
     @Deprecated
     public SelectionElement<T> getSelectionElement() {
     public SelectionElement<T> getSelectionElement() {
         return selectionElement;
         return selectionElement;
     }
     }
-    
+
     @Override
     @Override
     public Optional<T> getDefaultValue() {
     public Optional<T> getDefaultValue() {
         return defaultValue == null ? Optional.empty() : Optional.ofNullable(defaultValue.get());
         return defaultValue == null ? Optional.empty() : Optional.ofNullable(defaultValue.get());
     }
     }
-    
+
     @Override
     @Override
     public void save() {
     public void save() {
         if (saveConsumer != null)
         if (saveConsumer != null)
             saveConsumer.accept(getValue());
             saveConsumer.accept(getValue());
     }
     }
-    
+
     @Override
     @Override
     public List<? extends Element> children() {
     public List<? extends Element> children() {
         return Lists.newArrayList(selectionElement, resetButton);
         return Lists.newArrayList(selectionElement, resetButton);
     }
     }
-    
+
     @Override
     @Override
     public Optional<String> getError() {
     public Optional<String> getError() {
         return selectionElement.topRenderer.getError();
         return selectionElement.topRenderer.getError();
     }
     }
-    
+
     @Override
     @Override
     public void lateRender(int mouseX, int mouseY, float delta) {
     public void lateRender(int mouseX, int mouseY, float delta) {
         selectionElement.lateRender(mouseX, mouseY, delta);
         selectionElement.lateRender(mouseX, mouseY, delta);
     }
     }
-    
+
     @Override
     @Override
     public int getMorePossibleHeight() {
     public int getMorePossibleHeight() {
         return selectionElement.getMorePossibleHeight();
         return selectionElement.getMorePossibleHeight();
     }
     }
-    
+
     @Override
     @Override
     public boolean mouseScrolled(double double_1, double double_2, double double_3) {
     public boolean mouseScrolled(double double_1, double double_2, double double_3) {
         return selectionElement.mouseScrolled(double_1, double_2, double_3);
         return selectionElement.mouseScrolled(double_1, double_2, double_3);
     }
     }
-    
+
     public static class SelectionElement<R> extends AbstractParentElement implements Drawable {
     public static class SelectionElement<R> extends AbstractParentElement implements Drawable {
         protected Rectangle bounds;
         protected Rectangle bounds;
         protected boolean active;
         protected boolean active;
@@ -137,7 +139,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         protected DropdownBoxEntry<R> entry;
         protected DropdownBoxEntry<R> entry;
         protected DropdownMenuElement<R> menu;
         protected DropdownMenuElement<R> menu;
         protected boolean dontReFocus = false;
         protected boolean dontReFocus = false;
-        
+
         public SelectionElement(DropdownBoxEntry<R> entry, Rectangle bounds, DropdownMenuElement<R> menu, SelectionTopCellElement<R> topRenderer, SelectionCellCreator<R> cellCreator) {
         public SelectionElement(DropdownBoxEntry<R> entry, Rectangle bounds, DropdownMenuElement<R> menu, SelectionTopCellElement<R> topRenderer, SelectionCellCreator<R> cellCreator) {
             this.bounds = bounds;
             this.bounds = bounds;
             this.entry = entry;
             this.entry = entry;
@@ -148,48 +150,48 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             this.topRenderer = Objects.requireNonNull(topRenderer);
             this.topRenderer = Objects.requireNonNull(topRenderer);
             this.topRenderer.entry = entry;
             this.topRenderer.entry = entry;
         }
         }
-        
+
         @Override
         @Override
         public void render(int mouseX, int mouseY, float delta) {
         public void render(int mouseX, int mouseY, float delta) {
             fill(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, -6250336);
             fill(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, -6250336);
             fill(bounds.x + 1, bounds.y + 1, bounds.x + bounds.width - 1, bounds.y + bounds.height - 1, -16777216);
             fill(bounds.x + 1, bounds.y + 1, bounds.x + bounds.width - 1, bounds.y + bounds.height - 1, -16777216);
             topRenderer.render(mouseX, mouseY, bounds.x, bounds.y, bounds.width, bounds.height, delta);
             topRenderer.render(mouseX, mouseY, bounds.x, bounds.y, bounds.width, bounds.height, delta);
-            if (menu.isExpended())
+            if (menu.isExpanded())
                 menu.render(mouseX, mouseY, bounds, delta);
                 menu.render(mouseX, mouseY, bounds, delta);
         }
         }
-    
+
         @Deprecated
         @Deprecated
         public SelectionTopCellElement<R> getTopRenderer() {
         public SelectionTopCellElement<R> getTopRenderer() {
             return topRenderer;
             return topRenderer;
         }
         }
-    
+
         @Override
         @Override
         public boolean mouseScrolled(double double_1, double double_2, double double_3) {
         public boolean mouseScrolled(double double_1, double double_2, double double_3) {
-            if (menu.isExpended())
+            if (menu.isExpanded())
                 return menu.mouseScrolled(double_1, double_2, double_3);
                 return menu.mouseScrolled(double_1, double_2, double_3);
             return false;
             return false;
         }
         }
-        
+
         public void lateRender(int mouseX, int mouseY, float delta) {
         public void lateRender(int mouseX, int mouseY, float delta) {
-            if (menu.isExpended())
+            if (menu.isExpanded())
                 menu.lateRender(mouseX, mouseY, delta);
                 menu.lateRender(mouseX, mouseY, delta);
         }
         }
-        
+
         public int getMorePossibleHeight() {
         public int getMorePossibleHeight() {
-            if (menu.isExpended())
+            if (menu.isExpanded())
                 return menu.getHeight();
                 return menu.getHeight();
             return -1;
             return -1;
         }
         }
-        
+
         public R getValue() {
         public R getValue() {
             return topRenderer.getValue();
             return topRenderer.getValue();
         }
         }
-        
+
         @Override
         @Override
         public List<? extends Element> children() {
         public List<? extends Element> children() {
             return Lists.newArrayList(topRenderer, menu);
             return Lists.newArrayList(topRenderer, menu);
         }
         }
-        
+
         @Override
         @Override
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
             dontReFocus = false;
             dontReFocus = false;
@@ -201,90 +203,97 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             return b;
             return b;
         }
         }
     }
     }
-    
+
     public static abstract class DropdownMenuElement<R> extends AbstractParentElement {
     public static abstract class DropdownMenuElement<R> extends AbstractParentElement {
-        @Deprecated @Nonnull private SelectionCellCreator<R> cellCreator;
-        @Deprecated @Nonnull private DropdownBoxEntry<R> entry;
-        
-        @Nonnull
+        @Deprecated
+        @NotNull
+        private SelectionCellCreator<R> cellCreator;
+        @Deprecated
+        @NotNull
+        private DropdownBoxEntry<R> entry;
+
+        @NotNull
         public SelectionCellCreator<R> getCellCreator() {
         public SelectionCellCreator<R> getCellCreator() {
             return cellCreator;
             return cellCreator;
         }
         }
-        
-        @Nonnull
+
+        @NotNull
         public final DropdownBoxEntry<R> getEntry() {
         public final DropdownBoxEntry<R> getEntry() {
             return entry;
             return entry;
         }
         }
-        
-        @Nonnull
+
+        @NotNull
         public abstract ImmutableList<R> getSelections();
         public abstract ImmutableList<R> getSelections();
-        
+
         public abstract void initCells();
         public abstract void initCells();
-        
+
         public abstract void render(int mouseX, int mouseY, Rectangle rectangle, float delta);
         public abstract void render(int mouseX, int mouseY, Rectangle rectangle, float delta);
-        
+
         public abstract void lateRender(int mouseX, int mouseY, float delta);
         public abstract void lateRender(int mouseX, int mouseY, float delta);
-        
+
         public abstract int getHeight();
         public abstract int getHeight();
-        
-        public final boolean isExpended() {
+
+        public final boolean isExpanded() {
             return (getEntry().selectionElement.getFocused() == getEntry().selectionElement.topRenderer || getEntry().selectionElement.getFocused() == getEntry().selectionElement.menu) && getEntry().getFocused() == getEntry().selectionElement && getEntry().getParent().getFocused() == getEntry();
             return (getEntry().selectionElement.getFocused() == getEntry().selectionElement.topRenderer || getEntry().selectionElement.getFocused() == getEntry().selectionElement.menu) && getEntry().getFocused() == getEntry().selectionElement && getEntry().getParent().getFocused() == getEntry();
         }
         }
-        
+
         @Override
         @Override
         public abstract List<SelectionCellElement<R>> children();
         public abstract List<SelectionCellElement<R>> children();
     }
     }
-    
+
     public static class DefaultDropdownMenuElement<R> extends DropdownMenuElement<R> {
     public static class DefaultDropdownMenuElement<R> extends DropdownMenuElement<R> {
-        @Nonnull protected ImmutableList<R> selections;
-        @Nonnull protected List<SelectionCellElement<R>> cells;
-        @Nonnull protected List<SelectionCellElement<R>> currentElements;
+        @NotNull
+        protected ImmutableList<R> selections;
+        @NotNull
+        protected List<SelectionCellElement<R>> cells;
+        @NotNull
+        protected List<SelectionCellElement<R>> currentElements;
         protected String lastSearchKeyword = "";
         protected String lastSearchKeyword = "";
         protected Rectangle lastRectangle;
         protected Rectangle lastRectangle;
         protected boolean scrolling;
         protected boolean scrolling;
         protected double scroll, target;
         protected double scroll, target;
         protected long start;
         protected long start;
         protected long duration;
         protected long duration;
-        
-        public DefaultDropdownMenuElement(@Nonnull ImmutableList<R> selections) {
+
+        public DefaultDropdownMenuElement(@NotNull ImmutableList<R> selections) {
             this.selections = selections;
             this.selections = selections;
             this.cells = Lists.newArrayList();
             this.cells = Lists.newArrayList();
             this.currentElements = Lists.newArrayList();
             this.currentElements = Lists.newArrayList();
         }
         }
-        
+
         public final double clamp(double v) {
         public final double clamp(double v) {
             return MathHelper.clamp(v, -SmoothScrollingSettings.CLAMP_EXTENSION, getMaxScrollPosition() + SmoothScrollingSettings.CLAMP_EXTENSION);
             return MathHelper.clamp(v, -SmoothScrollingSettings.CLAMP_EXTENSION, getMaxScrollPosition() + SmoothScrollingSettings.CLAMP_EXTENSION);
         }
         }
-        
+
         public double getMaxScroll() {
         public double getMaxScroll() {
             return getCellCreator().getCellHeight() * currentElements.size();
             return getCellCreator().getCellHeight() * currentElements.size();
         }
         }
-        
+
         protected double getMaxScrollPosition() {
         protected double getMaxScrollPosition() {
             return Math.max(0, this.getMaxScroll() - (getHeight()));
             return Math.max(0, this.getMaxScroll() - (getHeight()));
         }
         }
-        
+
         @Override
         @Override
-        @Nonnull
+        @NotNull
         public ImmutableList<R> getSelections() {
         public ImmutableList<R> getSelections() {
             return selections;
             return selections;
         }
         }
-        
+
         @Override
         @Override
         public void initCells() {
         public void initCells() {
-            for(R selection : getSelections()) {
+            for (R selection : getSelections()) {
                 cells.add(getCellCreator().create(selection));
                 cells.add(getCellCreator().create(selection));
             }
             }
-            for(SelectionCellElement<R> cell : cells) {
+            for (SelectionCellElement<R> cell : cells) {
                 cell.entry = getEntry();
                 cell.entry = getEntry();
             }
             }
             search();
             search();
         }
         }
-        
+
         public void search() {
         public void search() {
             currentElements.clear();
             currentElements.clear();
             String keyword = this.lastSearchKeyword.toLowerCase();
             String keyword = this.lastSearchKeyword.toLowerCase();
-            for(SelectionCellElement<R> cell : cells) {
+            for (SelectionCellElement<R> cell : cells) {
                 String key = cell.getSearchKey();
                 String key = cell.getSearchKey();
                 if (key == null || key.toLowerCase().contains(keyword))
                 if (key == null || key.toLowerCase().contains(keyword))
                     currentElements.add(cell);
                     currentElements.add(cell);
@@ -295,15 +304,15 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             }
             }
             scrollTo(0, false);
             scrollTo(0, false);
         }
         }
-        
+
         protected int editDistance(String s1, String s2) {
         protected int editDistance(String s1, String s2) {
             s1 = s1.toLowerCase();
             s1 = s1.toLowerCase();
             s2 = s2.toLowerCase();
             s2 = s2.toLowerCase();
-            
+
             int[] costs = new int[s2.length() + 1];
             int[] costs = new int[s2.length() + 1];
-            for(int i = 0; i <= s1.length(); i++) {
+            for (int i = 0; i <= s1.length(); i++) {
                 int lastValue = i;
                 int lastValue = i;
-                for(int j = 0; j <= s2.length(); j++) {
+                for (int j = 0; j <= s2.length(); j++) {
                     if (i == 0)
                     if (i == 0)
                         costs[j] = j;
                         costs[j] = j;
                     else {
                     else {
@@ -321,7 +330,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             }
             }
             return costs[s2.length()];
             return costs[s2.length()];
         }
         }
-        
+
         protected double similarity(String s1, String s2) {
         protected double similarity(String s1, String s2) {
             String longer = s1, shorter = s2;
             String longer = s1, shorter = s2;
             if (s1.length() < s2.length()) { // longer should always have greater length
             if (s1.length() < s2.length()) { // longer should always have greater length
@@ -334,7 +343,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             }
             }
             return (longerLength - editDistance(longer, shorter)) / (double) longerLength;
             return (longerLength - editDistance(longer, shorter)) / (double) longerLength;
         }
         }
-        
+
         @Override
         @Override
         public void render(int mouseX, int mouseY, Rectangle rectangle, float delta) {
         public void render(int mouseX, int mouseY, Rectangle rectangle, float delta) {
             if (!getEntry().selectionElement.topRenderer.getSearchTerm().equals(lastSearchKeyword)) {
             if (!getEntry().selectionElement.topRenderer.getSearchTerm().equals(lastSearchKeyword)) {
@@ -345,7 +354,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             lastRectangle = rectangle.clone();
             lastRectangle = rectangle.clone();
             lastRectangle.translate(0, -1);
             lastRectangle.translate(0, -1);
         }
         }
-        
+
         private void updatePosition(float delta) {
         private void updatePosition(float delta) {
             target = clamp(target);
             target = clamp(target);
             if (target < 0) {
             if (target < 0) {
@@ -358,7 +367,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             else
             else
                 scroll = target;
                 scroll = target;
         }
         }
-        
+
         @Override
         @Override
         public void lateRender(int mouseX, int mouseY, float delta) {
         public void lateRender(int mouseX, int mouseY, float delta) {
             int last10Height = getHeight();
             int last10Height = getHeight();
@@ -367,10 +376,10 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             fill(lastRectangle.x + 1, lastRectangle.y + lastRectangle.height + 1, lastRectangle.x + cWidth - 1, lastRectangle.y + lastRectangle.height + last10Height, -16777216);
             fill(lastRectangle.x + 1, lastRectangle.y + lastRectangle.height + 1, lastRectangle.x + cWidth - 1, lastRectangle.y + lastRectangle.height + last10Height, -16777216);
             RenderSystem.pushMatrix();
             RenderSystem.pushMatrix();
             RenderSystem.translatef(0, 0, 300f);
             RenderSystem.translatef(0, 0, 300f);
-            
+
             ScissorsHandler.INSTANCE.scissor(new Rectangle(lastRectangle.x, lastRectangle.y + lastRectangle.height + 1, cWidth - 6, last10Height - 1));
             ScissorsHandler.INSTANCE.scissor(new Rectangle(lastRectangle.x, lastRectangle.y + lastRectangle.height + 1, cWidth - 6, last10Height - 1));
             double yy = lastRectangle.y + lastRectangle.height - scroll;
             double yy = lastRectangle.y + lastRectangle.height - scroll;
-            for(SelectionCellElement<R> cell : currentElements) {
+            for (SelectionCellElement<R> cell : currentElements) {
                 if (yy + getCellCreator().getCellHeight() >= lastRectangle.y + lastRectangle.height && yy <= lastRectangle.y + lastRectangle.height + last10Height + 1)
                 if (yy + getCellCreator().getCellHeight() >= lastRectangle.y + lastRectangle.height && yy <= lastRectangle.y + lastRectangle.height + last10Height + 1)
                     cell.render(mouseX, mouseY, lastRectangle.x, (int) yy, getMaxScrollPosition() > 6 ? getCellCreator().getCellWidth() - 6 : getCellCreator().getCellWidth(), getCellCreator().getCellHeight(), delta);
                     cell.render(mouseX, mouseY, lastRectangle.x, (int) yy, getMaxScrollPosition() > 6 ? getCellCreator().getCellWidth() - 6 : getCellCreator().getCellWidth(), getCellCreator().getCellHeight(), delta);
                 else
                 else
@@ -378,13 +387,13 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 yy += getCellCreator().getCellHeight();
                 yy += getCellCreator().getCellHeight();
             }
             }
             ScissorsHandler.INSTANCE.removeLastScissor();
             ScissorsHandler.INSTANCE.removeLastScissor();
-            
+
             if (currentElements.isEmpty()) {
             if (currentElements.isEmpty()) {
                 TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
                 TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
                 String s = I18n.translate("text.cloth-config.dropdown.value.unknown");
                 String s = I18n.translate("text.cloth-config.dropdown.value.unknown");
                 textRenderer.drawWithShadow(s, lastRectangle.x + getCellCreator().getCellWidth() / 2 - textRenderer.getStringWidth(s) / 2, lastRectangle.y + lastRectangle.height + 3, -1);
                 textRenderer.drawWithShadow(s, lastRectangle.x + getCellCreator().getCellWidth() / 2 - textRenderer.getStringWidth(s) / 2, lastRectangle.y + lastRectangle.height + 3, -1);
             }
             }
-            
+
             if (getMaxScrollPosition() > 6) {
             if (getMaxScrollPosition() > 6) {
                 RenderSystem.disableTexture();
                 RenderSystem.disableTexture();
                 int scrollbarPositionMinX = lastRectangle.x + getCellCreator().getCellWidth() - 6;
                 int scrollbarPositionMinX = lastRectangle.x + getCellCreator().getCellWidth() - 6;
@@ -394,13 +403,13 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 height -= Math.min((scroll < 0 ? (int) -scroll : scroll > getMaxScrollPosition() ? (int) scroll - getMaxScrollPosition() : 0), height * .95);
                 height -= Math.min((scroll < 0 ? (int) -scroll : scroll > getMaxScrollPosition() ? (int) scroll - getMaxScrollPosition() : 0), height * .95);
                 height = Math.max(10, height);
                 height = Math.max(10, height);
                 int minY = (int) Math.min(Math.max((int) scroll * (last10Height - height) / getMaxScrollPosition() + (lastRectangle.y + lastRectangle.height + 1), (lastRectangle.y + lastRectangle.height + 1)), (lastRectangle.y + lastRectangle.height + 1 + last10Height) - height);
                 int minY = (int) Math.min(Math.max((int) scroll * (last10Height - height) / getMaxScrollPosition() + (lastRectangle.y + lastRectangle.height + 1), (lastRectangle.y + lastRectangle.height + 1)), (lastRectangle.y + lastRectangle.height + 1 + last10Height) - height);
-                
+
                 int bottomc = new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height).contains(PointHelper.fromMouse()) ? 168 : 128;
                 int bottomc = new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height).contains(PointHelper.fromMouse()) ? 168 : 128;
                 int topc = new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height).contains(PointHelper.fromMouse()) ? 222 : 172;
                 int topc = new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height).contains(PointHelper.fromMouse()) ? 222 : 172;
-                
+
                 Tessellator tessellator = Tessellator.getInstance();
                 Tessellator tessellator = Tessellator.getInstance();
                 BufferBuilder buffer = tessellator.getBuffer();
                 BufferBuilder buffer = tessellator.getBuffer();
-                
+
                 // Bottom
                 // Bottom
                 buffer.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
                 buffer.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
                 buffer.vertex(scrollbarPositionMinX, minY + height, 0.0D).texture(0, 1).color(bottomc, bottomc, bottomc, 255).next();
                 buffer.vertex(scrollbarPositionMinX, minY + height, 0.0D).texture(0, 1).color(bottomc, bottomc, bottomc, 255).next();
@@ -408,7 +417,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 buffer.vertex(scrollbarPositionMaxX, minY, 0.0D).texture(1, 0).color(bottomc, bottomc, bottomc, 255).next();
                 buffer.vertex(scrollbarPositionMaxX, minY, 0.0D).texture(1, 0).color(bottomc, bottomc, bottomc, 255).next();
                 buffer.vertex(scrollbarPositionMinX, minY, 0.0D).texture(0, 0).color(bottomc, bottomc, bottomc, 255).next();
                 buffer.vertex(scrollbarPositionMinX, minY, 0.0D).texture(0, 0).color(bottomc, bottomc, bottomc, 255).next();
                 tessellator.draw();
                 tessellator.draw();
-                
+
                 // Top
                 // Top
                 buffer.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
                 buffer.begin(7, VertexFormats.POSITION_TEXTURE_COLOR);
                 buffer.vertex(scrollbarPositionMinX, (minY + height - 1), 0.0D).texture(0, 1).color(topc, topc, topc, 255).next();
                 buffer.vertex(scrollbarPositionMinX, (minY + height - 1), 0.0D).texture(0, 1).color(topc, topc, topc, 255).next();
@@ -421,20 +430,20 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             RenderSystem.translatef(0, 0, -300f);
             RenderSystem.translatef(0, 0, -300f);
             RenderSystem.popMatrix();
             RenderSystem.popMatrix();
         }
         }
-        
+
         @Override
         @Override
         public int getHeight() {
         public int getHeight() {
             return Math.max(Math.min(getCellCreator().getDropBoxMaxHeight(), (int) getMaxScroll()), 14);
             return Math.max(Math.min(getCellCreator().getDropBoxMaxHeight(), (int) getMaxScroll()), 14);
         }
         }
-        
+
         @Override
         @Override
         public boolean isMouseOver(double mouseX, double mouseY) {
         public boolean isMouseOver(double mouseX, double mouseY) {
-            return isExpended() && mouseX >= lastRectangle.x && mouseX <= lastRectangle.x + getCellCreator().getCellWidth() && mouseY >= lastRectangle.y + lastRectangle.height && mouseY <= lastRectangle.y + lastRectangle.height + getHeight() + 1;
+            return isExpanded() && mouseX >= lastRectangle.x && mouseX <= lastRectangle.x + getCellCreator().getCellWidth() && mouseY >= lastRectangle.y + lastRectangle.height && mouseY <= lastRectangle.y + lastRectangle.height + getHeight() + 1;
         }
         }
-        
+
         @Override
         @Override
         public boolean mouseDragged(double double_1, double double_2, int int_1, double double_3, double double_4) {
         public boolean mouseDragged(double double_1, double double_2, int int_1, double double_3, double double_4) {
-            if (!isExpended())
+            if (!isExpanded())
                 return false;
                 return false;
             if (int_1 == 0 && this.scrolling) {
             if (int_1 == 0 && this.scrolling) {
                 if (double_2 < (double) lastRectangle.y + lastRectangle.height) {
                 if (double_2 < (double) lastRectangle.y + lastRectangle.height) {
@@ -453,7 +462,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             }
             }
             return false;
             return false;
         }
         }
-        
+
         @Override
         @Override
         public boolean mouseScrolled(double mouseX, double mouseY, double double_3) {
         public boolean mouseScrolled(double mouseX, double mouseY, double double_3) {
             if (isMouseOver(mouseX, mouseY)) {
             if (isMouseOver(mouseX, mouseY)) {
@@ -462,101 +471,103 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             }
             }
             return false;
             return false;
         }
         }
-        
+
         protected void updateScrollingState(double double_1, double double_2, int int_1) {
         protected void updateScrollingState(double double_1, double double_2, int int_1) {
-            this.scrolling = isExpended() && lastRectangle != null && int_1 == 0 && double_1 >= (double) lastRectangle.x + getCellCreator().getCellWidth() - 6 && double_1 < (double) (lastRectangle.x + getCellCreator().getCellWidth());
+            this.scrolling = isExpanded() && lastRectangle != null && int_1 == 0 && double_1 >= (double) lastRectangle.x + getCellCreator().getCellWidth() - 6 && double_1 < (double) (lastRectangle.x + getCellCreator().getCellWidth());
         }
         }
-        
+
         @Override
         @Override
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
-            if (!isExpended())
+            if (!isExpanded())
                 return false;
                 return false;
             updateScrollingState(double_1, double_2, int_1);
             updateScrollingState(double_1, double_2, int_1);
             return super.mouseClicked(double_1, double_2, int_1) || scrolling;
             return super.mouseClicked(double_1, double_2, int_1) || scrolling;
         }
         }
-        
+
         public void offset(double value, boolean animated) {
         public void offset(double value, boolean animated) {
             scrollTo(target + value, animated);
             scrollTo(target + value, animated);
         }
         }
-        
+
         public void scrollTo(double value, boolean animated) {
         public void scrollTo(double value, boolean animated) {
             scrollTo(value, animated, ClothConfigInitializer.getScrollDuration());
             scrollTo(value, animated, ClothConfigInitializer.getScrollDuration());
         }
         }
-        
+
         public void scrollTo(double value, boolean animated, long duration) {
         public void scrollTo(double value, boolean animated, long duration) {
             target = clamp(value);
             target = clamp(value);
-            
+
             if (animated) {
             if (animated) {
                 start = System.currentTimeMillis();
                 start = System.currentTimeMillis();
                 this.duration = duration;
                 this.duration = duration;
             } else
             } else
                 scroll = target;
                 scroll = target;
         }
         }
-        
+
         @Override
         @Override
         public List<SelectionCellElement<R>> children() {
         public List<SelectionCellElement<R>> children() {
             return currentElements;
             return currentElements;
         }
         }
     }
     }
-    
+
     public static abstract class SelectionCellCreator<R> {
     public static abstract class SelectionCellCreator<R> {
         public abstract SelectionCellElement<R> create(R selection);
         public abstract SelectionCellElement<R> create(R selection);
-        
+
         public abstract int getCellHeight();
         public abstract int getCellHeight();
-        
+
         public abstract int getDropBoxMaxHeight();
         public abstract int getDropBoxMaxHeight();
-        
+
         public int getCellWidth() {
         public int getCellWidth() {
             return 132;
             return 132;
         }
         }
     }
     }
-    
+
     public static class DefaultSelectionCellCreator<R> extends SelectionCellCreator<R> {
     public static class DefaultSelectionCellCreator<R> extends SelectionCellCreator<R> {
         protected Function<R, String> toStringFunction;
         protected Function<R, String> toStringFunction;
-        
+
         public DefaultSelectionCellCreator(Function<R, String> toStringFunction) {
         public DefaultSelectionCellCreator(Function<R, String> toStringFunction) {
             this.toStringFunction = toStringFunction;
             this.toStringFunction = toStringFunction;
         }
         }
-        
+
         public DefaultSelectionCellCreator() {
         public DefaultSelectionCellCreator() {
             this(Object::toString);
             this(Object::toString);
         }
         }
-        
+
         @Override
         @Override
         public SelectionCellElement<R> create(R selection) {
         public SelectionCellElement<R> create(R selection) {
             return new DefaultSelectionCellElement<>(selection, toStringFunction);
             return new DefaultSelectionCellElement<>(selection, toStringFunction);
         }
         }
-        
+
         @Override
         @Override
         public int getCellHeight() {
         public int getCellHeight() {
             return 14;
             return 14;
         }
         }
-        
+
         @Override
         @Override
         public int getDropBoxMaxHeight() {
         public int getDropBoxMaxHeight() {
             return getCellHeight() * 7;
             return getCellHeight() * 7;
         }
         }
     }
     }
-    
+
     public static abstract class SelectionCellElement<R> extends AbstractParentElement {
     public static abstract class SelectionCellElement<R> extends AbstractParentElement {
-        @Deprecated @Nonnull private DropdownBoxEntry<R> entry;
-        
-        @Nonnull
+        @Deprecated
+        @NotNull
+        private DropdownBoxEntry<R> entry;
+
+        @NotNull
         public final DropdownBoxEntry<R> getEntry() {
         public final DropdownBoxEntry<R> getEntry() {
             return entry;
             return entry;
         }
         }
-        
+
         public abstract void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta);
         public abstract void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta);
-        
+
         public abstract void dontRender(float delta);
         public abstract void dontRender(float delta);
-        
+
         @Nullable
         @Nullable
         public abstract String getSearchKey();
         public abstract String getSearchKey();
-        
+
         @Nullable
         @Nullable
         public abstract R getSelection();
         public abstract R getSelection();
     }
     }
-    
+
     public static class DefaultSelectionCellElement<R> extends SelectionCellElement<R> {
     public static class DefaultSelectionCellElement<R> extends SelectionCellElement<R> {
         protected R r;
         protected R r;
         protected int x;
         protected int x;
@@ -565,12 +576,12 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         protected int height;
         protected int height;
         protected boolean rendering;
         protected boolean rendering;
         protected Function<R, String> toStringFunction;
         protected Function<R, String> toStringFunction;
-        
+
         public DefaultSelectionCellElement(R r, Function<R, String> toStringFunction) {
         public DefaultSelectionCellElement(R r, Function<R, String> toStringFunction) {
             this.r = r;
             this.r = r;
             this.toStringFunction = toStringFunction;
             this.toStringFunction = toStringFunction;
         }
         }
-        
+
         @Override
         @Override
         public void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta) {
         public void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta) {
             rendering = true;
             rendering = true;
@@ -583,29 +594,29 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 fill(x + 1, y + 1, x + width - 1, y + height - 1, -15132391);
                 fill(x + 1, y + 1, x + width - 1, y + height - 1, -15132391);
             MinecraftClient.getInstance().textRenderer.drawWithShadow(toStringFunction.apply(r), x + 6, y + 3, b ? 16777215 : 8947848);
             MinecraftClient.getInstance().textRenderer.drawWithShadow(toStringFunction.apply(r), x + 6, y + 3, b ? 16777215 : 8947848);
         }
         }
-        
+
         @Override
         @Override
         public void dontRender(float delta) {
         public void dontRender(float delta) {
             rendering = false;
             rendering = false;
         }
         }
-        
+
         @Nullable
         @Nullable
         @Override
         @Override
         public String getSearchKey() {
         public String getSearchKey() {
             return toStringFunction.apply(r);
             return toStringFunction.apply(r);
         }
         }
-        
+
         @Nullable
         @Nullable
         @Override
         @Override
         public R getSelection() {
         public R getSelection() {
             return r;
             return r;
         }
         }
-        
+
         @Override
         @Override
         public List<? extends Element> children() {
         public List<? extends Element> children() {
             return Collections.emptyList();
             return Collections.emptyList();
         }
         }
-        
+
         @Override
         @Override
         public boolean mouseClicked(double mouseX, double mouseY, int int_1) {
         public boolean mouseClicked(double mouseX, double mouseY, int int_1) {
             boolean b = rendering && mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height;
             boolean b = rendering && mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height;
@@ -618,37 +629,38 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             return false;
             return false;
         }
         }
     }
     }
-    
+
     public static abstract class SelectionTopCellElement<R> extends AbstractParentElement {
     public static abstract class SelectionTopCellElement<R> extends AbstractParentElement {
-        @Deprecated private DropdownBoxEntry<R> entry;
-        
+        @Deprecated
+        private DropdownBoxEntry<R> entry;
+
         public abstract R getValue();
         public abstract R getValue();
-        
+
         public abstract void setValue(R value);
         public abstract void setValue(R value);
-        
+
         public abstract String getSearchTerm();
         public abstract String getSearchTerm();
-        
+
         public abstract Optional<String> getError();
         public abstract Optional<String> getError();
-        
+
         public final Optional<String> getConfigError() {
         public final Optional<String> getConfigError() {
             return entry.getConfigError();
             return entry.getConfigError();
         }
         }
-        
+
         public DropdownBoxEntry<R> getParent() {
         public DropdownBoxEntry<R> getParent() {
             return entry;
             return entry;
         }
         }
-        
+
         public final boolean hasConfigError() {
         public final boolean hasConfigError() {
             return getConfigError().isPresent();
             return getConfigError().isPresent();
         }
         }
-        
+
         public final int getPreferredTextColor() {
         public final int getPreferredTextColor() {
             return getConfigError().isPresent() ? 16733525 : 16777215;
             return getConfigError().isPresent() ? 16733525 : 16777215;
         }
         }
-        
+
         public void selectFirstRecommendation() {
         public void selectFirstRecommendation() {
             List<SelectionCellElement<R>> children = getParent().selectionElement.menu.children();
             List<SelectionCellElement<R>> children = getParent().selectionElement.menu.children();
-            for(SelectionCellElement<R> child : children) {
+            for (SelectionCellElement<R> child : children) {
                 if (child.getSelection() != null) {
                 if (child.getSelection() != null) {
                     setValue(child.getSelection());
                     setValue(child.getSelection());
                     getParent().selectionElement.setFocused(null);
                     getParent().selectionElement.setFocused(null);
@@ -656,16 +668,16 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 }
                 }
             }
             }
         }
         }
-        
+
         public abstract void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta);
         public abstract void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta);
     }
     }
-    
+
     public static class DefaultSelectionTopCellElement<R> extends SelectionTopCellElement<R> {
     public static class DefaultSelectionTopCellElement<R> extends SelectionTopCellElement<R> {
         protected TextFieldWidget textFieldWidget;
         protected TextFieldWidget textFieldWidget;
         protected Function<String, R> toObjectFunction;
         protected Function<String, R> toObjectFunction;
         protected Function<R, String> toStringFunction;
         protected Function<R, String> toStringFunction;
         protected R value;
         protected R value;
-        
+
         public DefaultSelectionTopCellElement(R value, Function<String, R> toObjectFunction, Function<R, String> toStringFunction) {
         public DefaultSelectionTopCellElement(R value, Function<String, R> toObjectFunction, Function<R, String> toStringFunction) {
             this.value = Objects.requireNonNull(value);
             this.value = Objects.requireNonNull(value);
             this.toObjectFunction = Objects.requireNonNull(toObjectFunction);
             this.toObjectFunction = Objects.requireNonNull(toObjectFunction);
@@ -678,7 +690,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                     super.render(int_1, int_2, float_1);
                     super.render(int_1, int_2, float_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
-                
+
                 @Override
                 @Override
                 public boolean keyPressed(int int_1, int int_2, int int_3) {
                 public boolean keyPressed(int int_1, int int_2, int int_3) {
                     if (int_1 == 257 || int_1 == 335) {
                     if (int_1 == 257 || int_1 == 335) {
@@ -696,7 +708,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                     getParent().getScreen().setEdited(true, getParent().isRequiresRestart());
                     getParent().getScreen().setEdited(true, getParent().isRequiresRestart());
             });
             });
         }
         }
-        
+
         @Override
         @Override
         public void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta) {
         public void render(int mouseX, int mouseY, int x, int y, int width, int height, float delta) {
             textFieldWidget.x = x + 4;
             textFieldWidget.x = x + 4;
@@ -706,32 +718,32 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             textFieldWidget.setEditableColor(getPreferredTextColor());
             textFieldWidget.setEditableColor(getPreferredTextColor());
             textFieldWidget.render(mouseX, mouseY, delta);
             textFieldWidget.render(mouseX, mouseY, delta);
         }
         }
-        
+
         @Override
         @Override
         public R getValue() {
         public R getValue() {
             if (hasConfigError())
             if (hasConfigError())
                 return value;
                 return value;
             return toObjectFunction.apply(textFieldWidget.getText());
             return toObjectFunction.apply(textFieldWidget.getText());
         }
         }
-        
+
         @Override
         @Override
         public void setValue(R value) {
         public void setValue(R value) {
             textFieldWidget.setText(toStringFunction.apply(value));
             textFieldWidget.setText(toStringFunction.apply(value));
             textFieldWidget.setCursor(0);
             textFieldWidget.setCursor(0);
         }
         }
-        
+
         @Override
         @Override
         public String getSearchTerm() {
         public String getSearchTerm() {
             return textFieldWidget.getText();
             return textFieldWidget.getText();
         }
         }
-        
+
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             if (toObjectFunction.apply(textFieldWidget.getText()) != null)
             if (toObjectFunction.apply(textFieldWidget.getText()) != null)
                 return Optional.empty();
                 return Optional.empty();
             return Optional.of("Invalid Value!");
             return Optional.of("Invalid Value!");
         }
         }
-        
+
         @Override
         @Override
         public List<? extends Element> children() {
         public List<? extends Element> children() {
             return Collections.singletonList(textFieldWidget);
             return Collections.singletonList(textFieldWidget);

+ 56 - 109
src/main/java/me/shedaniel/clothconfig2/gui/entries/FloatListListEntry.java

@@ -1,123 +1,94 @@
 package me.shedaniel.clothconfig2.gui.entries;
 package me.shedaniel.clothconfig2.gui.entries;
 
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.gui.widget.TextFieldWidget;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 
-public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.FloatListCell> {
-    
+@ApiStatus.Internal
+public class FloatListListEntry extends AbstractTextFieldListListEntry<Float, FloatListListEntry.FloatListCell, FloatListListEntry> {
+
     private float minimum, maximum;
     private float minimum, maximum;
-    private Function<Float, Optional<String>> cellErrorSupplier;
-    
+
     @Deprecated
     @Deprecated
-    public FloatListListEntry(String fieldName, List<Float> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Float>> saveConsumer, Supplier<List<Float>> defaultValue, String resetButtonKey) {
-        this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
+    public FloatListListEntry(String fieldName, List<Float> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Float>> saveConsumer, Supplier<List<Float>> defaultValue, String resetButtonKey) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
-    public FloatListListEntry(String fieldName, List<Float> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Float>> saveConsumer, Supplier<List<Float>> defaultValue, String resetButtonKey, boolean requiresRestart) {
-        super(fieldName, tooltipSupplier, defaultValue, baseListEntry -> new FloatListCell(0, (FloatListListEntry) baseListEntry), saveConsumer, resetButtonKey, requiresRestart);
-        this.minimum = -Float.MAX_VALUE;
-        this.maximum = Float.MAX_VALUE;
-        for(float f : value)
-            cells.add(new FloatListCell(f, this));
-        this.widgets.addAll(cells);
-        expended = defaultExpended;
-    }
-    
-    public Function<Float, Optional<String>> getCellErrorSupplier() {
-        return cellErrorSupplier;
+    public FloatListListEntry(String fieldName, List<Float> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Float>> saveConsumer, Supplier<List<Float>> defaultValue, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, true, true);
     }
     }
-    
-    public void setCellErrorSupplier(Function<Float, Optional<String>> cellErrorSupplier) {
-        this.cellErrorSupplier = cellErrorSupplier;
-    }
-    
-    @Override
-    public List<Float> getValue() {
-        return cells.stream().map(FloatListCell::getValue).collect(Collectors.toList());
+
+    public FloatListListEntry(
+            String fieldName,
+            List<Float> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<Float>> saveConsumer,
+            Supplier<List<Float>> defaultValue,
+            String resetButtonKey,
+            boolean requiresRestart,
+            boolean deleteButtonEnabled,
+            boolean insertInFront
+    ) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, FloatListCell::new);
+        this.minimum = Float.NEGATIVE_INFINITY;
+        this.maximum = Float.POSITIVE_INFINITY;
     }
     }
-    
+
     public FloatListListEntry setMaximum(float maximum) {
     public FloatListListEntry setMaximum(float maximum) {
         this.maximum = maximum;
         this.maximum = maximum;
         return this;
         return this;
     }
     }
-    
+
     public FloatListListEntry setMinimum(float minimum) {
     public FloatListListEntry setMinimum(float minimum) {
         this.minimum = minimum;
         this.minimum = minimum;
         return this;
         return this;
     }
     }
-    
+
     @Override
     @Override
-    protected FloatListCell getFromValue(Float value) {
-        return new FloatListCell(value, this);
+    public FloatListListEntry self() {
+        return this;
     }
     }
-    
-    public static class FloatListCell extends BaseListCell {
-        
-        private Function<String, String> stripCharacters = s -> {
-            StringBuilder stringBuilder_1 = new StringBuilder();
-            char[] var2 = s.toCharArray();
-            int var3 = var2.length;
-            
-            for(int var4 = 0; var4 < var3; ++var4)
-                if (Character.isDigit(var2[var4]) || var2[var4] == '-' || var2[var4] == '.')
-                    stringBuilder_1.append(var2[var4]);
-            
-            return stringBuilder_1.toString();
-        };
-        private TextFieldWidget widget;
-        private boolean isSelected;
-        private FloatListListEntry listListEntry;
-        
-        public FloatListCell(float value, FloatListListEntry listListEntry) {
-            this.listListEntry = listListEntry;
-            this.setErrorSupplier(() -> listListEntry.cellErrorSupplier == null ? Optional.empty() : listListEntry.getCellErrorSupplier().apply(getValue()));
-            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
-                @Override
-                public void render(int int_1, int int_2, float float_1) {
-                    boolean f = isFocused();
-                    setFocused(isSelected);
-                    widget.setEditableColor(getPreferredTextColor());
-                    super.render(int_1, int_2, float_1);
-                    setFocused(f);
-                }
-                
-                @Override
-                public void write(String string_1) {
-                    super.write(stripCharacters.apply(string_1));
-                }
-            };
-            widget.setMaxLength(999999);
-            widget.setHasBorder(false);
-            widget.setText(value + "");
-            widget.setChangedListener(s -> {
-                if (!(value + "").equalsIgnoreCase(s))
-                    listListEntry.getScreen().setEdited(true, listListEntry.isRequiresRestart());
-            });
+
+    public static class FloatListCell extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<Float, FloatListCell, FloatListListEntry> {
+
+        public FloatListCell(Float value, FloatListListEntry listListEntry) {
+            super(value, listListEntry);
+        }
+
+        @Nullable
+        @Override
+        protected Float substituteDefault(@Nullable Float value) {
+            if (value == null)
+                return 0f;
+            else
+                return value;
         }
         }
-        
-        public float getValue() {
+
+        @Override
+        protected boolean isValidText(@NotNull String text) {
+            return text.chars().allMatch(c -> Character.isDigit(c) || c == '-' || c == '.');
+        }
+
+        public Float getValue() {
             try {
             try {
                 return Float.valueOf(widget.getText());
                 return Float.valueOf(widget.getText());
             } catch (NumberFormatException e) {
             } catch (NumberFormatException e) {
                 return 0f;
                 return 0f;
             }
             }
         }
         }
-        
+
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
-                float i = Float.valueOf(widget.getText());
+                float i = Float.parseFloat(widget.getText());
                 if (i > listListEntry.maximum)
                 if (i > listListEntry.maximum)
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                 else if (i < listListEntry.minimum)
                 else if (i < listListEntry.minimum)
@@ -127,29 +98,5 @@ public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.
             }
             }
             return Optional.empty();
             return Optional.empty();
         }
         }
-        
-        @Override
-        public int getCellHeight() {
-            return 20;
-        }
-        
-        @Override
-        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-            widget.setWidth(entryWidth - 12);
-            widget.x = x;
-            widget.y = y + 1;
-            widget.setEditable(listListEntry.isEditable());
-            this.isSelected = isSelected;
-            widget.render(mouseX, mouseY, delta);
-            if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
-        }
-        
-        @Override
-        public List<? extends Element> children() {
-            return Collections.singletonList(widget);
-        }
-        
     }
     }
-    
 }
 }

+ 57 - 109
src/main/java/me/shedaniel/clothconfig2/gui/entries/IntegerListListEntry.java

@@ -1,123 +1,94 @@
 package me.shedaniel.clothconfig2.gui.entries;
 package me.shedaniel.clothconfig2.gui.entries;
 
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.gui.widget.TextFieldWidget;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 
-public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListListEntry.IntegerListCell> {
-    
+@ApiStatus.Internal
+public class IntegerListListEntry extends AbstractTextFieldListListEntry<Integer, IntegerListListEntry.IntegerListCell, IntegerListListEntry> {
+
     private int minimum, maximum;
     private int minimum, maximum;
-    private Function<Integer, Optional<String>> cellErrorSupplier;
-    
+
     @Deprecated
     @Deprecated
-    public IntegerListListEntry(String fieldName, List<Integer> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Integer>> saveConsumer, Supplier<List<Integer>> defaultValue, String resetButtonKey) {
-        this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
+    public IntegerListListEntry(String fieldName, List<Integer> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Integer>> saveConsumer, Supplier<List<Integer>> defaultValue, String resetButtonKey) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
-    public IntegerListListEntry(String fieldName, List<Integer> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Integer>> saveConsumer, Supplier<List<Integer>> defaultValue, String resetButtonKey, boolean requiresRestart) {
-        super(fieldName, tooltipSupplier, defaultValue, baseListEntry -> new IntegerListCell(0, (IntegerListListEntry) baseListEntry), saveConsumer, resetButtonKey, requiresRestart);
-        this.minimum = -Integer.MAX_VALUE;
-        this.maximum = Integer.MAX_VALUE;
-        for(int integer : value)
-            cells.add(new IntegerListCell(integer, this));
-        this.widgets.addAll(cells);
-        expended = defaultExpended;
+    public IntegerListListEntry(String fieldName, List<Integer> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Integer>> saveConsumer, Supplier<List<Integer>> defaultValue, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, true, true);
     }
     }
-    
-    public Function<Integer, Optional<String>> getCellErrorSupplier() {
-        return cellErrorSupplier;
-    }
-    
-    public void setCellErrorSupplier(Function<Integer, Optional<String>> cellErrorSupplier) {
-        this.cellErrorSupplier = cellErrorSupplier;
-    }
-    
-    @Override
-    public List<Integer> getValue() {
-        return cells.stream().map(IntegerListCell::getValue).collect(Collectors.toList());
+
+    public IntegerListListEntry(
+            String fieldName,
+            List<Integer> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<Integer>> saveConsumer,
+            Supplier<List<Integer>> defaultValue,
+            String resetButtonKey,
+            boolean requiresRestart,
+            boolean deleteButtonEnabled,
+            boolean insertInFront
+    ) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, IntegerListCell::new);
+        this.minimum = Integer.MIN_VALUE;
+        this.maximum = Integer.MAX_VALUE;
     }
     }
-    
+
     public IntegerListListEntry setMaximum(int maximum) {
     public IntegerListListEntry setMaximum(int maximum) {
         this.maximum = maximum;
         this.maximum = maximum;
         return this;
         return this;
     }
     }
-    
+
     public IntegerListListEntry setMinimum(int minimum) {
     public IntegerListListEntry setMinimum(int minimum) {
         this.minimum = minimum;
         this.minimum = minimum;
         return this;
         return this;
     }
     }
-    
+
     @Override
     @Override
-    protected IntegerListCell getFromValue(Integer value) {
-        return new IntegerListCell(value, this);
+    public IntegerListListEntry self() {
+        return this;
     }
     }
-    
-    public static class IntegerListCell extends BaseListCell {
-        
-        private Function<String, String> stripCharacters = s -> {
-            StringBuilder stringBuilder_1 = new StringBuilder();
-            char[] var2 = s.toCharArray();
-            int var3 = var2.length;
-            
-            for(int var4 = 0; var4 < var3; ++var4)
-                if (Character.isDigit(var2[var4]) || var2[var4] == '-')
-                    stringBuilder_1.append(var2[var4]);
-            
-            return stringBuilder_1.toString();
-        };
-        private TextFieldWidget widget;
-        private boolean isSelected;
-        private IntegerListListEntry listListEntry;
-        
-        public IntegerListCell(int value, IntegerListListEntry listListEntry) {
-            this.listListEntry = listListEntry;
-            this.setErrorSupplier(() -> listListEntry.cellErrorSupplier == null ? Optional.empty() : listListEntry.getCellErrorSupplier().apply(getValue()));
-            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
-                @Override
-                public void render(int int_1, int int_2, float float_1) {
-                    boolean f = isFocused();
-                    setFocused(isSelected);
-                    widget.setEditableColor(getPreferredTextColor());
-                    super.render(int_1, int_2, float_1);
-                    setFocused(f);
-                }
-                
-                @Override
-                public void write(String string_1) {
-                    super.write(stripCharacters.apply(string_1));
-                }
-            };
-            widget.setMaxLength(999999);
-            widget.setHasBorder(false);
-            widget.setText(value + "");
-            widget.setChangedListener(s -> {
-                if (!(value + "").equalsIgnoreCase(s))
-                    listListEntry.getScreen().setEdited(true, listListEntry.isRequiresRestart());
-            });
+
+    public static class IntegerListCell extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<Integer, IntegerListCell, IntegerListListEntry> {
+
+        public IntegerListCell(Integer value, IntegerListListEntry listListEntry) {
+            super(value, listListEntry);
         }
         }
-        
-        public int getValue() {
+
+        @Nullable
+        @Override
+        protected Integer substituteDefault(@Nullable Integer value) {
+            if (value == null)
+                return 0;
+            else
+                return value;
+        }
+
+        @Override
+        protected boolean isValidText(@NotNull String text) {
+            return text.chars().allMatch(c -> Character.isDigit(c) || c == '-');
+        }
+
+        public Integer getValue() {
             try {
             try {
                 return Integer.valueOf(widget.getText());
                 return Integer.valueOf(widget.getText());
             } catch (NumberFormatException e) {
             } catch (NumberFormatException e) {
                 return 0;
                 return 0;
             }
             }
         }
         }
-        
+
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
-                int i = Integer.valueOf(widget.getText());
+                int i = Integer.parseInt(widget.getText());
                 if (i > listListEntry.maximum)
                 if (i > listListEntry.maximum)
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                 else if (i < listListEntry.minimum)
                 else if (i < listListEntry.minimum)
@@ -127,29 +98,6 @@ public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListList
             }
             }
             return Optional.empty();
             return Optional.empty();
         }
         }
-        
-        @Override
-        public int getCellHeight() {
-            return 20;
-        }
-        
-        @Override
-        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-            widget.setWidth(entryWidth - 12);
-            widget.x = x;
-            widget.y = y + 1;
-            widget.setEditable(listListEntry.isEditable());
-            this.isSelected = isSelected;
-            widget.render(mouseX, mouseY, delta);
-            if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
-        }
-        
-        @Override
-        public List<? extends Element> children() {
-            return Collections.singletonList(widget);
-        }
-        
     }
     }
-    
+
 }
 }

+ 47 - 110
src/main/java/me/shedaniel/clothconfig2/gui/entries/LongListListEntry.java

@@ -1,123 +1,83 @@
 package me.shedaniel.clothconfig2.gui.entries;
 package me.shedaniel.clothconfig2.gui.entries;
 
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.gui.widget.TextFieldWidget;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 
-public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.LongListCell> {
-    
+@ApiStatus.Internal
+public class LongListListEntry extends AbstractTextFieldListListEntry<Long, LongListListEntry.LongListCell, LongListListEntry> {
+
     private long minimum, maximum;
     private long minimum, maximum;
-    private Function<Long, Optional<String>> cellErrorSupplier;
-    
+
     @Deprecated
     @Deprecated
-    public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey) {
-        this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
+    public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
-    public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey, boolean requiresRestart) {
-        super(fieldName, tooltipSupplier, defaultValue, baseListEntry -> new LongListCell(0, (LongListListEntry) baseListEntry), saveConsumer, resetButtonKey, requiresRestart);
-        this.minimum = -Long.MAX_VALUE;
-        this.maximum = Long.MAX_VALUE;
-        for(long l : value)
-            cells.add(new LongListCell(l, this));
-        this.widgets.addAll(cells);
-        expended = defaultExpended;
+    public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, true, true);
     }
     }
-    
-    public Function<Long, Optional<String>> getCellErrorSupplier() {
-        return cellErrorSupplier;
-    }
-    
-    public void setCellErrorSupplier(Function<Long, Optional<String>> cellErrorSupplier) {
-        this.cellErrorSupplier = cellErrorSupplier;
-    }
-    
-    @Override
-    public List<Long> getValue() {
-        return cells.stream().map(LongListCell::getValue).collect(Collectors.toList());
+
+    public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey, boolean requiresRestart, boolean deleteButtonEnabled, boolean insertInFront) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, LongListCell::new);
+        this.minimum = Long.MIN_VALUE;
+        this.maximum = Long.MAX_VALUE;
     }
     }
-    
+
     public LongListListEntry setMaximum(long maximum) {
     public LongListListEntry setMaximum(long maximum) {
         this.maximum = maximum;
         this.maximum = maximum;
         return this;
         return this;
     }
     }
-    
+
     public LongListListEntry setMinimum(long minimum) {
     public LongListListEntry setMinimum(long minimum) {
         this.minimum = minimum;
         this.minimum = minimum;
         return this;
         return this;
     }
     }
-    
+
     @Override
     @Override
-    protected LongListCell getFromValue(Long value) {
-        return new LongListCell(value, this);
+    public LongListListEntry self() {
+        return this;
     }
     }
-    
-    public static class LongListCell extends BaseListCell {
-        
-        private Function<String, String> stripCharacters = s -> {
-            StringBuilder stringBuilder_1 = new StringBuilder();
-            char[] var2 = s.toCharArray();
-            int var3 = var2.length;
-            
-            for(int var4 = 0; var4 < var3; ++var4)
-                if (Character.isDigit(var2[var4]) || var2[var4] == '-')
-                    stringBuilder_1.append(var2[var4]);
-            
-            return stringBuilder_1.toString();
-        };
-        private TextFieldWidget widget;
-        private boolean isSelected;
-        private LongListListEntry listListEntry;
-        
-        public LongListCell(long value, LongListListEntry listListEntry) {
-            this.listListEntry = listListEntry;
-            this.setErrorSupplier(() -> listListEntry.cellErrorSupplier == null ? Optional.empty() : listListEntry.getCellErrorSupplier().apply(getValue()));
-            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
-                @Override
-                public void render(int int_1, int int_2, float float_1) {
-                    boolean f = isFocused();
-                    setFocused(isSelected);
-                    widget.setEditableColor(getPreferredTextColor());
-                    super.render(int_1, int_2, float_1);
-                    setFocused(f);
-                }
-                
-                @Override
-                public void write(String string_1) {
-                    super.write(stripCharacters.apply(string_1));
-                }
-            };
-            widget.setMaxLength(999999);
-            widget.setHasBorder(false);
-            widget.setText(value + "");
-            widget.setChangedListener(s -> {
-                if (!(value + "").equalsIgnoreCase(s))
-                    listListEntry.getScreen().setEdited(true, listListEntry.isRequiresRestart());
-            });
+
+    public static class LongListCell extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<Long, LongListCell, LongListListEntry> {
+
+        public LongListCell(Long value, LongListListEntry listListEntry) {
+            super(value, listListEntry);
         }
         }
-        
-        public long getValue() {
+
+        @Nullable
+        @Override
+        protected Long substituteDefault(@Nullable Long value) {
+            if (value == null)
+                return 0L;
+            else
+                return value;
+        }
+
+        @Override
+        protected boolean isValidText(@NotNull String text) {
+            return text.chars().allMatch(c -> Character.isDigit(c) || c == '-');
+        }
+
+        public Long getValue() {
             try {
             try {
                 return Long.valueOf(widget.getText());
                 return Long.valueOf(widget.getText());
             } catch (NumberFormatException e) {
             } catch (NumberFormatException e) {
-                return 0;
+                return 0L;
             }
             }
         }
         }
-        
+
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
-                long l = Long.valueOf(widget.getText());
+                long l = Long.parseLong(widget.getText());
                 if (l > listListEntry.maximum)
                 if (l > listListEntry.maximum)
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                     return Optional.of(I18n.translate("text.cloth-config.error.too_large", listListEntry.maximum));
                 else if (l < listListEntry.minimum)
                 else if (l < listListEntry.minimum)
@@ -127,29 +87,6 @@ public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.Lon
             }
             }
             return Optional.empty();
             return Optional.empty();
         }
         }
-        
-        @Override
-        public int getCellHeight() {
-            return 20;
-        }
-        
-        @Override
-        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-            widget.setWidth(entryWidth - 12);
-            widget.x = x;
-            widget.y = y + 1;
-            widget.setEditable(listListEntry.isEditable());
-            this.isSelected = isSelected;
-            widget.render(mouseX, mouseY, delta);
-            if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
-        }
-        
-        @Override
-        public List<? extends Element> children() {
-            return Collections.singletonList(widget);
-        }
-        
     }
     }
-    
+
 }
 }

+ 152 - 0
src/main/java/me/shedaniel/clothconfig2/gui/entries/MultiElementListEntry.java

@@ -0,0 +1,152 @@
+package me.shedaniel.clothconfig2.gui.entries;
+
+import com.google.common.collect.Lists;
+import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
+import me.shedaniel.math.api.Rectangle;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.render.DiffuseLighting;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.client.sound.PositionedSoundInstance;
+import net.minecraft.sound.SoundEvents;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class MultiElementListEntry<T> extends TooltipListEntry<T> {
+
+    private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
+    private final T object;
+    private String categoryName;
+    private List<AbstractConfigListEntry<?>> entries;
+    private MultiElementListEntry<T>.CategoryLabelWidget widget;
+    private List<Element> children;
+    private boolean expanded;
+
+    @ApiStatus.Internal
+    public MultiElementListEntry(String categoryName, T object, List<AbstractConfigListEntry<?>> entries, boolean defaultExpanded) {
+        super(categoryName, null);
+        this.categoryName = categoryName;
+        this.object = object;
+        this.entries = entries;
+        this.expanded = defaultExpanded;
+        this.widget = new MultiElementListEntry<T>.CategoryLabelWidget();
+        this.children = Lists.newArrayList(widget);
+        this.children.addAll(entries);
+    }
+
+    @Override
+    public boolean isRequiresRestart() {
+        for (AbstractConfigListEntry entry : entries)
+            if (entry.isRequiresRestart())
+                return true;
+        return false;
+    }
+
+    @Override
+    public void setRequiresRestart(boolean requiresRestart) {
+
+    }
+
+    public String getCategoryName() {
+        return categoryName;
+    }
+
+    @Override
+    public T getValue() {
+        return object;
+    }
+
+    @Override
+    public Optional<T> getDefaultValue() {
+        return Optional.empty();
+    }
+
+    @Override
+    public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
+        super.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
+        widget.rectangle.x = x - 19;
+        widget.rectangle.y = y;
+        widget.rectangle.width = entryWidth + 19;
+        widget.rectangle.height = 24;
+        MinecraftClient.getInstance().getTextureManager().bindTexture(CONFIG_TEX);
+        DiffuseLighting.disable();
+        RenderSystem.color4f(1, 1, 1, 1);
+        blit(x - 15, y + 4, 24, (widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (expanded ? 9 : 0), 9, 9);
+        MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(categoryName), x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
+        for (AbstractConfigListEntry<?> entry : entries) {
+            entry.setParent(getParent());
+            entry.setScreen(getScreen());
+        }
+        if (expanded) {
+            int yy = y + 24;
+            for (AbstractConfigListEntry<?> entry : entries) {
+                entry.render(-1, yy, x + 14, entryWidth - 14, entry.getItemHeight(), mouseX, mouseY, isSelected, delta);
+                yy += entry.getItemHeight();
+            }
+        }
+    }
+
+    @Override
+    public boolean isMouseInside(int mouseX, int mouseY, int x, int y, int entryWidth, int entryHeight) {
+        widget.rectangle.x = x - 15;
+        widget.rectangle.y = y;
+        widget.rectangle.width = entryWidth + 15;
+        widget.rectangle.height = 24;
+        return widget.rectangle.contains(mouseX, mouseY) && getParent().isMouseOver(mouseX, mouseY);
+    }
+
+    @Override
+    public int getItemHeight() {
+        if (expanded) {
+            int i = 24;
+            for (AbstractConfigListEntry<?> entry : entries)
+                i += entry.getItemHeight();
+            return i;
+        }
+        return 24;
+    }
+
+    @Override
+    public List<? extends Element> children() {
+        return children;
+    }
+
+    @Override
+    public void save() {
+        entries.forEach(AbstractConfigListEntry::save);
+    }
+
+    @Override
+    public Optional<String> getError() {
+        List<String> errors = entries.stream()
+                .map(AbstractConfigListEntry::getConfigError)
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .collect(Collectors.toList());
+
+        if (errors.size() > 1)
+            return Optional.of(I18n.translate("text.cloth-config.multi_error"));
+        else
+            return errors.stream().findFirst();
+    }
+
+    public class CategoryLabelWidget implements Element {
+        private Rectangle rectangle = new Rectangle();
+
+        @Override
+        public boolean mouseClicked(double double_1, double double_2, int int_1) {
+            if (rectangle.contains(double_1, double_2)) {
+                expanded = !expanded;
+                MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+                return true;
+            }
+            return false;
+        }
+    }
+
+}

+ 109 - 0
src/main/java/me/shedaniel/clothconfig2/gui/entries/NestedListListEntry.java

@@ -0,0 +1,109 @@
+package me.shedaniel.clothconfig2.gui.entries;
+
+import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
+import me.shedaniel.clothconfig2.gui.entries.NestedListListEntry.NestedListCell;
+import net.minecraft.client.gui.Element;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * @param <T>     the configuration object type
+ * @param <INNER> the type of the inner config entry
+ */
+public final class NestedListListEntry<T, INNER extends AbstractConfigListEntry<T>> extends AbstractListListEntry<T, NestedListCell<T, INNER>, NestedListListEntry<T, INNER>> {
+
+    @ApiStatus.Internal
+    public NestedListListEntry(
+            String fieldName,
+            List<T> value,
+            boolean defaultExpanded,
+            Supplier<Optional<String[]>> tooltipSupplier,
+            Consumer<List<T>> saveConsumer,
+            Supplier<List<T>> defaultValue,
+            String resetButtonKey,
+            boolean deleteButtonEnabled,
+            boolean insertInFront,
+            BiFunction<T, NestedListListEntry<T, INNER>, INNER> createNewCell
+    ) {
+        super(
+                fieldName,
+                value,
+                defaultExpanded,
+                null,
+                null,
+                defaultValue,
+                resetButtonKey,
+                false,
+                deleteButtonEnabled,
+                insertInFront,
+                (t, nestedListListEntry) -> new NestedListCell<>(t, nestedListListEntry, createNewCell.apply(t, nestedListListEntry))
+        );
+    }
+
+    @Override
+    public boolean isRequiresRestart() {
+        return cells.stream().anyMatch(NestedListCell::isRequiresRestart);
+    }
+
+    @Override
+    public void setRequiresRestart(boolean requiresRestart) {
+    }
+
+    @Override
+    public NestedListListEntry<T, INNER> self() {
+        return this;
+    }
+
+    /**
+     * @param <T> the configuration object type
+     * @see NestedListListEntry
+     */
+    public static class NestedListCell<T, INNER extends AbstractConfigListEntry<T>> extends AbstractListListEntry.AbstractListCell<T, NestedListCell<T, INNER>, NestedListListEntry<T, INNER>> {
+
+        private final INNER nestedEntry;
+
+        @ApiStatus.Internal
+        public NestedListCell(@Nullable T value, NestedListListEntry<T, INNER> listListEntry, INNER nestedEntry) {
+            super(value, listListEntry);
+            this.nestedEntry = nestedEntry;
+        }
+
+        @Override
+        public T getValue() {
+            return nestedEntry.getValue();
+        }
+
+        @Override
+        public Optional<String> getError() {
+            return nestedEntry.getError();
+        }
+
+        @Override
+        public int getCellHeight() {
+            return nestedEntry.getItemHeight();
+        }
+
+        @Override
+        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
+            nestedEntry.setScreen(listListEntry.getScreen());
+            nestedEntry.render(index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
+        }
+
+        @Override
+        public List<? extends Element> children() {
+            return Collections.singletonList(nestedEntry);
+        }
+
+        private boolean isRequiresRestart() {
+            return nestedEntry.isRequiresRestart();
+        }
+    }
+
+}

+ 2 - 2
src/main/java/me/shedaniel/clothconfig2/gui/entries/SelectionListEntry.java

@@ -7,8 +7,8 @@ import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.widget.ButtonWidget;
 import net.minecraft.client.gui.widget.ButtonWidget;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.util.Window;
 import net.minecraft.client.util.Window;
+import org.jetbrains.annotations.NotNull;
 
 
-import javax.annotation.Nonnull;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -120,7 +120,7 @@ public class SelectionListEntry<T> extends TooltipListEntry<T> {
     }
     }
     
     
     public static interface Translatable {
     public static interface Translatable {
-        @Nonnull
+        @NotNull
         String getKey();
         String getKey();
     }
     }
     
     

+ 39 - 81
src/main/java/me/shedaniel/clothconfig2/gui/entries/StringListListEntry.java

@@ -1,108 +1,66 @@
 package me.shedaniel.clothconfig2.gui.entries;
 package me.shedaniel.clothconfig2.gui.entries;
 
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.gui.widget.TextFieldWidget;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 
-public class StringListListEntry extends BaseListEntry<String, StringListListEntry.StringListCell> {
-    
-    private Function<String, Optional<String>> cellErrorSupplier;
-    
+@ApiStatus.Internal
+public class StringListListEntry extends AbstractTextFieldListListEntry<String, StringListListEntry.StringListCell, StringListListEntry> {
+
     @Deprecated
     @Deprecated
-    public StringListListEntry(String fieldName, List<String> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<String>> saveConsumer, Supplier<List<String>> defaultValue, String resetButtonKey) {
-        this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
+    public StringListListEntry(String fieldName, List<String> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<String>> saveConsumer, Supplier<List<String>> defaultValue, String resetButtonKey) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
-    public StringListListEntry(String fieldName, List<String> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<String>> saveConsumer, Supplier<List<String>> defaultValue, String resetButtonKey, boolean requiresRestart) {
-        super(fieldName, tooltipSupplier, defaultValue, baseListEntry -> new StringListCell("", (StringListListEntry) baseListEntry), saveConsumer, resetButtonKey, requiresRestart);
-        for(String str : value)
-            cells.add(new StringListCell(str, this));
-        this.widgets.addAll(cells);
-        expended = defaultExpended;
-    }
-    
-    public Function<String, Optional<String>> getCellErrorSupplier() {
-        return cellErrorSupplier;
+    public StringListListEntry(String fieldName, List<String> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<String>> saveConsumer, Supplier<List<String>> defaultValue, String resetButtonKey, boolean requiresRestart) {
+        this(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, true, true);
     }
     }
-    
-    public void setCellErrorSupplier(Function<String, Optional<String>> cellErrorSupplier) {
-        this.cellErrorSupplier = cellErrorSupplier;
-    }
-    
-    @Override
-    public List<String> getValue() {
-        return cells.stream().map(cell -> cell.widget.getText()).collect(Collectors.toList());
+
+    public StringListListEntry(String fieldName, List<String> value, boolean defaultExpanded, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<String>> saveConsumer, Supplier<List<String>> defaultValue, String resetButtonKey, boolean requiresRestart, boolean deleteButtonEnabled, boolean insertInFront) {
+        super(fieldName, value, defaultExpanded, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, requiresRestart, deleteButtonEnabled, insertInFront, StringListCell::new);
     }
     }
-    
+
     @Override
     @Override
-    protected StringListCell getFromValue(String value) {
-        return new StringListCell(value, this);
+    public StringListListEntry self() {
+        return this;
     }
     }
-    
-    public static class StringListCell extends BaseListCell {
-        
-        private TextFieldWidget widget;
-        private boolean isSelected;
-        private StringListListEntry listListEntry;
-        
+
+    public static class StringListCell extends AbstractTextFieldListListEntry.AbstractTextFieldListCell<String, StringListCell, StringListListEntry> {
+
         public StringListCell(String value, StringListListEntry listListEntry) {
         public StringListCell(String value, StringListListEntry listListEntry) {
-            this.listListEntry = listListEntry;
-            this.setErrorSupplier(() -> listListEntry.cellErrorSupplier == null ? Optional.empty() : listListEntry.getCellErrorSupplier().apply(widget.getText()));
-            widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
-                @Override
-                public void render(int int_1, int int_2, float float_1) {
-                    boolean f = isFocused();
-                    setFocused(isSelected);
-                    widget.setEditableColor(getPreferredTextColor());
-                    super.render(int_1, int_2, float_1);
-                    setFocused(f);
-                }
-            };
-            widget.setMaxLength(999999);
-            widget.setHasBorder(false);
-            widget.setText(value);
-            widget.setChangedListener(s -> {
-                if (!value.contentEquals(s))
-                    listListEntry.getScreen().setEdited(true, listListEntry.isRequiresRestart());
-            });
+            super(value, listListEntry);
         }
         }
-        
+
+        @Nullable
         @Override
         @Override
-        public Optional<String> getError() {
-            return Optional.empty();
+        protected String substituteDefault(@Nullable String value) {
+            if (value == null)
+                return "";
+            else
+                return value;
         }
         }
-        
+
         @Override
         @Override
-        public int getCellHeight() {
-            return 20;
+        protected boolean isValidText(@NotNull String text) {
+            return true;
         }
         }
-        
+
         @Override
         @Override
-        public void render(int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
-            widget.setWidth(entryWidth - 12);
-            widget.x = x;
-            widget.y = y + 1;
-            widget.setEditable(listListEntry.isEditable());
-            this.isSelected = isSelected;
-            widget.render(mouseX, mouseY, delta);
-            if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+        public String getValue() {
+            return widget.getText();
         }
         }
-        
+
         @Override
         @Override
-        public List<? extends Element> children() {
-            return Collections.singletonList(widget);
+        public Optional<String> getError() {
+            return Optional.empty();
         }
         }
-        
+
     }
     }
-    
+
 }
 }

+ 11 - 11
src/main/java/me/shedaniel/clothconfig2/gui/entries/SubCategoryListEntry.java

@@ -16,20 +16,20 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 
 
 public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigListEntry>> {
 public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigListEntry>> {
-    
+
     private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
     private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
     private String categoryName;
     private String categoryName;
     private List<AbstractConfigListEntry> entries;
     private List<AbstractConfigListEntry> entries;
     private CategoryLabelWidget widget;
     private CategoryLabelWidget widget;
     private List<Element> children;
     private List<Element> children;
-    private boolean expended;
-    
+    private boolean expanded;
+
     @Deprecated
     @Deprecated
-    public SubCategoryListEntry(String categoryName, List<AbstractConfigListEntry> entries, boolean defaultExpended) {
+    public SubCategoryListEntry(String categoryName, List<AbstractConfigListEntry> entries, boolean defaultExpanded) {
         super(categoryName, null);
         super(categoryName, null);
         this.categoryName = categoryName;
         this.categoryName = categoryName;
         this.entries = entries;
         this.entries = entries;
-        this.expended = defaultExpended;
+        this.expanded = defaultExpanded;
         this.widget = new CategoryLabelWidget();
         this.widget = new CategoryLabelWidget();
         this.children = Lists.newArrayList(widget);
         this.children = Lists.newArrayList(widget);
         this.children.addAll(entries);
         this.children.addAll(entries);
@@ -72,15 +72,15 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
         MinecraftClient.getInstance().getTextureManager().bindTexture(CONFIG_TEX);
         MinecraftClient.getInstance().getTextureManager().bindTexture(CONFIG_TEX);
         DiffuseLighting.disable();
         DiffuseLighting.disable();
         RenderSystem.color4f(1, 1, 1, 1);
         RenderSystem.color4f(1, 1, 1, 1);
-        blit(x - 15, y + 4, 24, (widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (expended ? 9 : 0), 9, 9);
+        blit(x - 15, y + 4, 24, (widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (expanded ? 9 : 0), 9, 9);
         MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(categoryName), x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
         MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(categoryName), x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
         for(AbstractConfigListEntry entry : entries) {
         for(AbstractConfigListEntry entry : entries) {
             entry.setParent(getParent());
             entry.setParent(getParent());
             entry.setScreen(getScreen());
             entry.setScreen(getScreen());
         }
         }
-        if (expended) {
+        if (expanded) {
             int yy = y + 24;
             int yy = y + 24;
-            for(AbstractConfigListEntry entry : entries) {
+            for (AbstractConfigListEntry entry : entries) {
                 entry.render(-1, yy, x + 14, entryWidth - 14, entry.getItemHeight(), mouseX, mouseY, isSelected, delta);
                 entry.render(-1, yy, x + 14, entryWidth - 14, entry.getItemHeight(), mouseX, mouseY, isSelected, delta);
                 yy += entry.getItemHeight();
                 yy += entry.getItemHeight();
             }
             }
@@ -98,9 +98,9 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
     
     
     @Override
     @Override
     public int getItemHeight() {
     public int getItemHeight() {
-        if (expended) {
+        if (expanded) {
             int i = 24;
             int i = 24;
-            for(AbstractConfigListEntry entry : entries)
+            for (AbstractConfigListEntry entry : entries)
                 i += entry.getItemHeight();
                 i += entry.getItemHeight();
             return i;
             return i;
         }
         }
@@ -135,7 +135,7 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
         @Override
         @Override
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
             if (rectangle.contains(double_1, double_2)) {
             if (rectangle.contains(double_1, double_2)) {
-                expended = !expended;
+                expanded = !expanded;
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 return true;
                 return true;
             }
             }

+ 7 - 6
src/main/java/me/shedaniel/clothconfig2/gui/entries/TooltipListEntry.java

@@ -3,23 +3,24 @@ package me.shedaniel.clothconfig2.gui.entries;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.QueuedTooltip;
 import me.shedaniel.clothconfig2.api.QueuedTooltip;
 import me.shedaniel.math.api.Point;
 import me.shedaniel.math.api.Point;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nullable;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public abstract class TooltipListEntry<T> extends AbstractConfigListEntry<T> {
 public abstract class TooltipListEntry<T> extends AbstractConfigListEntry<T> {
-    
-    @Nullable private Supplier<Optional<String[]>> tooltipSupplier;
-    
+
+    @Nullable
+    private Supplier<Optional<String[]>> tooltipSupplier;
+
     @Deprecated
     @Deprecated
     public TooltipListEntry(String fieldName, @Nullable Supplier<Optional<String[]>> tooltipSupplier) {
     public TooltipListEntry(String fieldName, @Nullable Supplier<Optional<String[]>> tooltipSupplier) {
         this(fieldName, tooltipSupplier, false);
         this(fieldName, tooltipSupplier, false);
     }
     }
-    
+
     @Deprecated
     @Deprecated
     public TooltipListEntry(String fieldName,
     public TooltipListEntry(String fieldName,
-            @Nullable Supplier<Optional<String[]>> tooltipSupplier, boolean requiresRestart) {
+                            @Nullable Supplier<Optional<String[]>> tooltipSupplier, boolean requiresRestart) {
         super(fieldName, requiresRestart);
         super(fieldName, requiresRestart);
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
     }
     }

+ 18 - 20
src/main/java/me/shedaniel/clothconfig2/impl/ScissorsHandlerImpl.java

@@ -1,11 +1,10 @@
 package me.shedaniel.clothconfig2.impl;
 package me.shedaniel.clothconfig2.impl;
 
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
+import fudge.notenoughcrashes.api.MinecraftCrashes;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
 import me.shedaniel.clothconfig2.api.ScissorsHandler;
 import me.shedaniel.clothconfig2.api.ScissorsHandler;
-import me.shedaniel.math.api.Executor;
 import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.api.Rectangle;
-import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.util.Window;
 import net.minecraft.client.util.Window;
 import org.lwjgl.opengl.GL11;
 import org.lwjgl.opengl.GL11;
@@ -14,51 +13,50 @@ import java.util.Collections;
 import java.util.List;
 import java.util.List;
 
 
 public final class ScissorsHandlerImpl implements ScissorsHandler {
 public final class ScissorsHandlerImpl implements ScissorsHandler {
-    
-    @Deprecated public static final ScissorsHandler INSTANCE = new ScissorsHandlerImpl();
-    
+
+    @Deprecated
+    public static final ScissorsHandler INSTANCE = new ScissorsHandlerImpl();
+
     static {
     static {
-        Executor.runIf(() -> FabricLoader.getInstance().isModLoaded("notenoughcrashes"), () -> () -> {
-            fudge.notenoughcrashes.api.NotEnoughCrashesApi.onEveryCrash(() -> {
-                try {
-                    ScissorsHandler.INSTANCE.clearScissors();
-                } catch (Throwable t) {
-                    ClothConfigInitializer.LOGGER.error("[ClothConfig] Failed clear scissors on game crash!", t);
-                }
-            });
+        MinecraftCrashes.onEveryCrash(() -> {
+            try {
+                ScissorsHandler.INSTANCE.clearScissors();
+            } catch (Throwable t) {
+                ClothConfigInitializer.LOGGER.error("[ClothConfig] Failed clear scissors on game crash!", t);
+            }
         });
         });
     }
     }
-    
+
     private List<Rectangle> scissorsAreas;
     private List<Rectangle> scissorsAreas;
-    
+
     public ScissorsHandlerImpl() {
     public ScissorsHandlerImpl() {
         this.scissorsAreas = Lists.newArrayList();
         this.scissorsAreas = Lists.newArrayList();
     }
     }
-    
+
     @Override
     @Override
     public void clearScissors() {
     public void clearScissors() {
         scissorsAreas.clear();
         scissorsAreas.clear();
         applyScissors();
         applyScissors();
     }
     }
-    
+
     @Override
     @Override
     public List<Rectangle> getScissorsAreas() {
     public List<Rectangle> getScissorsAreas() {
         return Collections.unmodifiableList(scissorsAreas);
         return Collections.unmodifiableList(scissorsAreas);
     }
     }
-    
+
     @Override
     @Override
     public void scissor(Rectangle rectangle) {
     public void scissor(Rectangle rectangle) {
         scissorsAreas.add(rectangle);
         scissorsAreas.add(rectangle);
         applyScissors();
         applyScissors();
     }
     }
-    
+
     @Override
     @Override
     public void removeLastScissor() {
     public void removeLastScissor() {
         if (!scissorsAreas.isEmpty())
         if (!scissorsAreas.isEmpty())
             scissorsAreas.remove(scissorsAreas.size() - 1);
             scissorsAreas.remove(scissorsAreas.size() - 1);
         applyScissors();
         applyScissors();
     }
     }
-    
+
     @Override
     @Override
     public void applyScissors() {
     public void applyScissors() {
         if (!scissorsAreas.isEmpty()) {
         if (!scissorsAreas.isEmpty()) {

+ 27 - 23
src/main/java/me/shedaniel/clothconfig2/impl/builders/BooleanToggleBuilder.java

@@ -1,81 +1,85 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.BooleanListEntry;
 import me.shedaniel.clothconfig2.gui.entries.BooleanListEntry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class BooleanToggleBuilder extends FieldBuilder<Boolean, BooleanListEntry> {
 public class BooleanToggleBuilder extends FieldBuilder<Boolean, BooleanListEntry> {
-    
-    @Nullable private Consumer<Boolean> saveConsumer = null;
-    @Nonnull private Function<Boolean, Optional<String[]>> tooltipSupplier = bool -> Optional.empty();
+
+    @Nullable
+    private Consumer<Boolean> saveConsumer = null;
+    @NotNull
+    private Function<Boolean, Optional<String[]>> tooltipSupplier = bool -> Optional.empty();
     private boolean value;
     private boolean value;
-    @Nullable private Function<Boolean, String> yesNoTextSupplier = null;
-    
+    @Nullable
+    private Function<Boolean, String> yesNoTextSupplier = null;
+
     public BooleanToggleBuilder(String resetButtonKey, String fieldNameKey, boolean value) {
     public BooleanToggleBuilder(String resetButtonKey, String fieldNameKey, boolean value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public BooleanToggleBuilder setErrorSupplier(@Nullable Function<Boolean, Optional<String>> errorSupplier) {
     public BooleanToggleBuilder setErrorSupplier(@Nullable Function<Boolean, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder requireRestart() {
     public BooleanToggleBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder setSaveConsumer(Consumer<Boolean> saveConsumer) {
     public BooleanToggleBuilder setSaveConsumer(Consumer<Boolean> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder setDefaultValue(Supplier<Boolean> defaultValue) {
     public BooleanToggleBuilder setDefaultValue(Supplier<Boolean> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder setDefaultValue(boolean defaultValue) {
     public BooleanToggleBuilder setDefaultValue(boolean defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
-    public BooleanToggleBuilder setTooltipSupplier(@Nonnull Function<Boolean, Optional<String[]>> tooltipSupplier) {
+
+    public BooleanToggleBuilder setTooltipSupplier(@NotNull Function<Boolean, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
-    public BooleanToggleBuilder setTooltipSupplier(@Nonnull Supplier<Optional<String[]>> tooltipSupplier) {
+
+    public BooleanToggleBuilder setTooltipSupplier(@NotNull Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = bool -> tooltipSupplier.get();
         this.tooltipSupplier = bool -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder setTooltip(Optional<String[]> tooltip) {
     public BooleanToggleBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = bool -> tooltip;
         this.tooltipSupplier = bool -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public BooleanToggleBuilder setTooltip(@Nullable String... tooltip) {
     public BooleanToggleBuilder setTooltip(@Nullable String... tooltip) {
         this.tooltipSupplier = bool -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = bool -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     @Nullable
     @Nullable
     public Function<Boolean, String> getYesNoTextSupplier() {
     public Function<Boolean, String> getYesNoTextSupplier() {
         return yesNoTextSupplier;
         return yesNoTextSupplier;
     }
     }
-    
+
     public BooleanToggleBuilder setYesNoTextSupplier(@Nullable Function<Boolean, String> yesNoTextSupplier) {
     public BooleanToggleBuilder setYesNoTextSupplier(@Nullable Function<Boolean, String> yesNoTextSupplier) {
         this.yesNoTextSupplier = yesNoTextSupplier;
         this.yesNoTextSupplier = yesNoTextSupplier;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public BooleanListEntry build() {
     public BooleanListEntry build() {
         BooleanListEntry entry = new BooleanListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart()) {
         BooleanListEntry entry = new BooleanListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart()) {
@@ -91,5 +95,5 @@ public class BooleanToggleBuilder extends FieldBuilder<Boolean, BooleanListEntry
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.DoubleListEntry;
 import me.shedaniel.clothconfig2.gui.entries.DoubleListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -73,17 +74,18 @@ public class DoubleFieldBuilder extends FieldBuilder<Double, DoubleListEntry> {
         this.tooltipSupplier = d -> tooltipSupplier.get();
         this.tooltipSupplier = d -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public DoubleFieldBuilder setTooltip(Optional<String[]> tooltip) {
     public DoubleFieldBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = d -> tooltip;
         this.tooltipSupplier = d -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public DoubleFieldBuilder setTooltip(String... tooltip) {
     public DoubleFieldBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = d -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = d -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public DoubleListEntry build() {
     public DoubleListEntry build() {
         DoubleListEntry entry = new DoubleListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         DoubleListEntry entry = new DoubleListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -97,4 +99,4 @@ public class DoubleFieldBuilder extends FieldBuilder<Double, DoubleListEntry> {
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 34 - 43
src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleListBuilder.java

@@ -1,8 +1,8 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
-import me.shedaniel.clothconfig2.gui.entries.BaseListEntry;
 import me.shedaniel.clothconfig2.gui.entries.DoubleListListEntry;
 import me.shedaniel.clothconfig2.gui.entries.DoubleListListEntry;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
@@ -11,139 +11,130 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class DoubleListBuilder extends FieldBuilder<List<Double>, DoubleListListEntry> {
 public class DoubleListBuilder extends FieldBuilder<List<Double>, DoubleListListEntry> {
-    
+
     protected Function<Double, Optional<String>> cellErrorSupplier;
     protected Function<Double, Optional<String>> cellErrorSupplier;
     private Consumer<List<Double>> saveConsumer = null;
     private Consumer<List<Double>> saveConsumer = null;
     private Function<List<Double>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<Double>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private List<Double> value;
     private List<Double> value;
-    private boolean expended = false;
+    private boolean expanded = false;
     private Double min = null, max = null;
     private Double min = null, max = null;
-    private Function<BaseListEntry, DoubleListListEntry.DoubleListCell> createNewInstance;
+    private Function<DoubleListListEntry, DoubleListListEntry.DoubleListCell> createNewInstance;
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private boolean deleteButtonEnabled = true, insertInFront = true;
     private boolean deleteButtonEnabled = true, insertInFront = true;
-    
+
     public DoubleListBuilder(String resetButtonKey, String fieldNameKey, List<Double> value) {
     public DoubleListBuilder(String resetButtonKey, String fieldNameKey, List<Double> value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public Function<Double, Optional<String>> getCellErrorSupplier() {
     public Function<Double, Optional<String>> getCellErrorSupplier() {
         return cellErrorSupplier;
         return cellErrorSupplier;
     }
     }
-    
+
     public DoubleListBuilder setCellErrorSupplier(Function<Double, Optional<String>> cellErrorSupplier) {
     public DoubleListBuilder setCellErrorSupplier(Function<Double, Optional<String>> cellErrorSupplier) {
         this.cellErrorSupplier = cellErrorSupplier;
         this.cellErrorSupplier = cellErrorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setErrorSupplier(Function<List<Double>, Optional<String>> errorSupplier) {
     public DoubleListBuilder setErrorSupplier(Function<List<Double>, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public DoubleListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setInsertInFront(boolean insertInFront) {
     public DoubleListBuilder setInsertInFront(boolean insertInFront) {
         this.insertInFront = insertInFront;
         this.insertInFront = insertInFront;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setAddButtonTooltip(String addTooltip) {
     public DoubleListBuilder setAddButtonTooltip(String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setRemoveButtonTooltip(String removeTooltip) {
     public DoubleListBuilder setRemoveButtonTooltip(String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder requireRestart() {
     public DoubleListBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
-    public DoubleListBuilder setCreateNewInstance(Function<BaseListEntry, DoubleListListEntry.DoubleListCell> createNewInstance) {
+
+    public DoubleListBuilder setCreateNewInstance(Function<DoubleListListEntry, DoubleListListEntry.DoubleListCell> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         return this;
         return this;
     }
     }
-    
-    public DoubleListBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public DoubleListBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setSaveConsumer(Consumer<List<Double>> saveConsumer) {
     public DoubleListBuilder setSaveConsumer(Consumer<List<Double>> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setDefaultValue(Supplier<List<Double>> defaultValue) {
     public DoubleListBuilder setDefaultValue(Supplier<List<Double>> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setMin(double min) {
     public DoubleListBuilder setMin(double min) {
         this.min = min;
         this.min = min;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setMax(double max) {
     public DoubleListBuilder setMax(double max) {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder removeMin() {
     public DoubleListBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder removeMax() {
     public DoubleListBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setDefaultValue(List<Double> defaultValue) {
     public DoubleListBuilder setDefaultValue(List<Double> defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setTooltipSupplier(Function<List<Double>, Optional<String[]>> tooltipSupplier) {
     public DoubleListBuilder setTooltipSupplier(Function<List<Double>, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
     public DoubleListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = list -> tooltipSupplier.get();
         this.tooltipSupplier = list -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setTooltip(Optional<String[]> tooltip) {
     public DoubleListBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public DoubleListBuilder setTooltip(String... tooltip) {
     public DoubleListBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public DoubleListListEntry build() {
     public DoubleListListEntry build() {
-        DoubleListListEntry entry = new DoubleListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart()) {
-            @Override
-            public boolean isDeleteButtonEnabled() {
-                return deleteButtonEnabled;
-            }
-            
-            @Override
-            public boolean insertInFront() {
-                return insertInFront;
-            }
-        };
+        DoubleListListEntry entry = new DoubleListListEntry(getFieldNameKey(), value, expanded, null, saveConsumer, defaultValue, getResetButtonKey(), requireRestart, deleteButtonEnabled, insertInFront);
         if (min != null)
         if (min != null)
             entry.setMinimum(min);
             entry.setMinimum(min);
         if (max != null)
         if (max != null)
@@ -158,5 +149,5 @@ public class DoubleListBuilder extends FieldBuilder<List<Double>, DoubleListList
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 6 - 6
src/main/java/me/shedaniel/clothconfig2/impl/builders/DropdownMenuBuilder.java

@@ -13,8 +13,8 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.item.Items;
 import net.minecraft.item.Items;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.registry.Registry;
 import net.minecraft.util.registry.Registry;
+import org.jetbrains.annotations.NotNull;
 
 
-import javax.annotation.Nonnull;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
@@ -74,18 +74,18 @@ public class DropdownMenuBuilder<T> extends FieldBuilder<T, DropdownBoxEntry<T>>
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     public DropdownMenuBuilder<T> requireRestart() {
     public DropdownMenuBuilder<T> requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
+
     public DropdownMenuBuilder<T> setErrorSupplier(Function<T, Optional<String>> errorSupplier) {
     public DropdownMenuBuilder<T> setErrorSupplier(Function<T, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
-    @Nonnull
+
+    @NotNull
     @Override
     @Override
     public DropdownBoxEntry<T> build() {
     public DropdownBoxEntry<T> build() {
         DropdownBoxEntry<T> entry = new DropdownBoxEntry<T>(getFieldNameKey(), getResetButtonKey(), null, isRequireRestart(), defaultValue, saveConsumer, selections, topCellElement, cellCreator);
         DropdownBoxEntry<T> entry = new DropdownBoxEntry<T>(getFieldNameKey(), getResetButtonKey(), null, isRequireRestart(), defaultValue, saveConsumer, selections, topCellElement, cellCreator);
@@ -94,7 +94,7 @@ public class DropdownMenuBuilder<T> extends FieldBuilder<T, DropdownBoxEntry<T>>
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
+
     public static class TopCellElementBuilder {
     public static class TopCellElementBuilder {
         public static final Function<String, Identifier> IDENTIFIER_FUNCTION = str -> {
         public static final Function<String, Identifier> IDENTIFIER_FUNCTION = str -> {
             try {
             try {

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/EnumSelectorBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.EnumListEntry;
 import me.shedaniel.clothconfig2.gui.entries.EnumListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
@@ -64,18 +65,19 @@ public class EnumSelectorBuilder<T extends Enum<?>> extends FieldBuilder<T, Enum
         this.tooltipSupplier = e -> tooltip;
         this.tooltipSupplier = e -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public EnumSelectorBuilder<T> setTooltip(String... tooltip) {
     public EnumSelectorBuilder<T> setTooltip(String... tooltip) {
         this.tooltipSupplier = e -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = e -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     public EnumSelectorBuilder<T> setEnumNameProvider(Function<Enum, String> enumNameProvider) {
     public EnumSelectorBuilder<T> setEnumNameProvider(Function<Enum, String> enumNameProvider) {
         Objects.requireNonNull(enumNameProvider);
         Objects.requireNonNull(enumNameProvider);
         this.enumNameProvider = enumNameProvider;
         this.enumNameProvider = enumNameProvider;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public EnumListEntry<T> build() {
     public EnumListEntry<T> build() {
         EnumListEntry<T> entry = new EnumListEntry<>(getFieldNameKey(), clazz, value, getResetButtonKey(), defaultValue, saveConsumer, enumNameProvider, null, isRequireRestart());
         EnumListEntry<T> entry = new EnumListEntry<>(getFieldNameKey(), clazz, value, getResetButtonKey(), defaultValue, saveConsumer, enumNameProvider, null, isRequireRestart());
@@ -84,5 +86,5 @@ public class EnumSelectorBuilder<T extends Enum<?>> extends FieldBuilder<T, Enum
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
+
 }
 }

+ 24 - 19
src/main/java/me/shedaniel/clothconfig2/impl/builders/FieldBuilder.java

@@ -1,55 +1,60 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public abstract class FieldBuilder<T, A extends AbstractConfigListEntry> {
 public abstract class FieldBuilder<T, A extends AbstractConfigListEntry> {
-    @Nonnull private final String fieldNameKey;
-    @Nonnull private final String resetButtonKey;
+    @NotNull
+    private final String fieldNameKey;
+    @NotNull
+    private final String resetButtonKey;
     protected boolean requireRestart = false;
     protected boolean requireRestart = false;
-    @Nullable protected Supplier<T> defaultValue = null;
-    @Nullable protected Function<T, Optional<String>> errorSupplier;
-    
+    @Nullable
+    protected Supplier<T> defaultValue = null;
+    @Nullable
+    protected Function<T, Optional<String>> errorSupplier;
+
     protected FieldBuilder(String resetButtonKey, String fieldNameKey) {
     protected FieldBuilder(String resetButtonKey, String fieldNameKey) {
         this.resetButtonKey = Objects.requireNonNull(resetButtonKey);
         this.resetButtonKey = Objects.requireNonNull(resetButtonKey);
         this.fieldNameKey = Objects.requireNonNull(fieldNameKey);
         this.fieldNameKey = Objects.requireNonNull(fieldNameKey);
     }
     }
-    
+
     @Nullable
     @Nullable
     public final Supplier<T> getDefaultValue() {
     public final Supplier<T> getDefaultValue() {
         return defaultValue;
         return defaultValue;
     }
     }
-    
+
+    @SuppressWarnings("rawtypes")
     @Deprecated
     @Deprecated
     public final AbstractConfigListEntry buildEntry() {
     public final AbstractConfigListEntry buildEntry() {
         return build();
         return build();
     }
     }
-    
-    @Nonnull
+
+    @NotNull
     public abstract A build();
     public abstract A build();
-    
-    @Nonnull
+
+    @NotNull
     public final String getFieldNameKey() {
     public final String getFieldNameKey() {
         return fieldNameKey;
         return fieldNameKey;
     }
     }
-    
-    @Nonnull
+
+    @NotNull
     public final String getResetButtonKey() {
     public final String getResetButtonKey() {
         return resetButtonKey;
         return resetButtonKey;
     }
     }
-    
+
     public boolean isRequireRestart() {
     public boolean isRequireRestart() {
         return requireRestart;
         return requireRestart;
     }
     }
-    
+
     public void requireRestart(boolean requireRestart) {
     public void requireRestart(boolean requireRestart) {
         this.requireRestart = requireRestart;
         this.requireRestart = requireRestart;
     }
     }
-    
-}
+
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.FloatListEntry;
 import me.shedaniel.clothconfig2.gui.entries.FloatListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -73,17 +74,18 @@ public class FloatFieldBuilder extends FieldBuilder<Float, FloatListEntry> {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public FloatFieldBuilder removeMin() {
     public FloatFieldBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public FloatFieldBuilder removeMax() {
     public FloatFieldBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public FloatListEntry build() {
     public FloatListEntry build() {
         FloatListEntry entry = new FloatListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         FloatListEntry entry = new FloatListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -97,4 +99,4 @@ public class FloatFieldBuilder extends FieldBuilder<Float, FloatListEntry> {
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 34 - 43
src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatListBuilder.java

@@ -1,8 +1,8 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
-import me.shedaniel.clothconfig2.gui.entries.BaseListEntry;
 import me.shedaniel.clothconfig2.gui.entries.FloatListListEntry;
 import me.shedaniel.clothconfig2.gui.entries.FloatListListEntry;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
@@ -11,139 +11,130 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class FloatListBuilder extends FieldBuilder<List<Float>, FloatListListEntry> {
 public class FloatListBuilder extends FieldBuilder<List<Float>, FloatListListEntry> {
-    
+
     protected Function<Float, Optional<String>> cellErrorSupplier;
     protected Function<Float, Optional<String>> cellErrorSupplier;
     private Consumer<List<Float>> saveConsumer = null;
     private Consumer<List<Float>> saveConsumer = null;
     private Function<List<Float>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<Float>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private List<Float> value;
     private List<Float> value;
-    private boolean expended = false;
+    private boolean expanded = false;
     private Float min = null, max = null;
     private Float min = null, max = null;
-    private Function<BaseListEntry, FloatListListEntry.FloatListCell> createNewInstance;
+    private Function<FloatListListEntry, FloatListListEntry.FloatListCell> createNewInstance;
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private boolean deleteButtonEnabled = true, insertInFront = true;
     private boolean deleteButtonEnabled = true, insertInFront = true;
-    
+
     public FloatListBuilder(String resetButtonKey, String fieldNameKey, List<Float> value) {
     public FloatListBuilder(String resetButtonKey, String fieldNameKey, List<Float> value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public Function<Float, Optional<String>> getCellErrorSupplier() {
     public Function<Float, Optional<String>> getCellErrorSupplier() {
         return cellErrorSupplier;
         return cellErrorSupplier;
     }
     }
-    
+
     public FloatListBuilder setCellErrorSupplier(Function<Float, Optional<String>> cellErrorSupplier) {
     public FloatListBuilder setCellErrorSupplier(Function<Float, Optional<String>> cellErrorSupplier) {
         this.cellErrorSupplier = cellErrorSupplier;
         this.cellErrorSupplier = cellErrorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public FloatListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setErrorSupplier(Function<List<Float>, Optional<String>> errorSupplier) {
     public FloatListBuilder setErrorSupplier(Function<List<Float>, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setInsertInFront(boolean insertInFront) {
     public FloatListBuilder setInsertInFront(boolean insertInFront) {
         this.insertInFront = insertInFront;
         this.insertInFront = insertInFront;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setAddButtonTooltip(String addTooltip) {
     public FloatListBuilder setAddButtonTooltip(String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setRemoveButtonTooltip(String removeTooltip) {
     public FloatListBuilder setRemoveButtonTooltip(String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder requireRestart() {
     public FloatListBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
-    public FloatListBuilder setCreateNewInstance(Function<BaseListEntry, FloatListListEntry.FloatListCell> createNewInstance) {
+
+    public FloatListBuilder setCreateNewInstance(Function<FloatListListEntry, FloatListListEntry.FloatListCell> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         return this;
         return this;
     }
     }
-    
-    public FloatListBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public FloatListBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setSaveConsumer(Consumer<List<Float>> saveConsumer) {
     public FloatListBuilder setSaveConsumer(Consumer<List<Float>> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setDefaultValue(Supplier<List<Float>> defaultValue) {
     public FloatListBuilder setDefaultValue(Supplier<List<Float>> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setMin(float min) {
     public FloatListBuilder setMin(float min) {
         this.min = min;
         this.min = min;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setMax(float max) {
     public FloatListBuilder setMax(float max) {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder removeMin() {
     public FloatListBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder removeMax() {
     public FloatListBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setDefaultValue(List<Float> defaultValue) {
     public FloatListBuilder setDefaultValue(List<Float> defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
     public FloatListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = list -> tooltipSupplier.get();
         this.tooltipSupplier = list -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setTooltipSupplier(Function<List<Float>, Optional<String[]>> tooltipSupplier) {
     public FloatListBuilder setTooltipSupplier(Function<List<Float>, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setTooltip(Optional<String[]> tooltip) {
     public FloatListBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public FloatListBuilder setTooltip(String... tooltip) {
     public FloatListBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public FloatListListEntry build() {
     public FloatListListEntry build() {
-        FloatListListEntry entry = new FloatListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart()) {
-            @Override
-            public boolean isDeleteButtonEnabled() {
-                return deleteButtonEnabled;
-            }
-            
-            @Override
-            public boolean insertInFront() {
-                return insertInFront;
-            }
-        };
+        FloatListListEntry entry = new FloatListListEntry(getFieldNameKey(), value, expanded, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart(), deleteButtonEnabled, insertInFront);
         if (min != null)
         if (min != null)
             entry.setMinimum(min);
             entry.setMinimum(min);
         if (max != null)
         if (max != null)
@@ -158,5 +149,5 @@ public class FloatListBuilder extends FieldBuilder<List<Float>, FloatListListEnt
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/IntFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.IntegerListEntry;
 import me.shedaniel.clothconfig2.gui.entries.IntegerListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -73,17 +74,18 @@ public class IntFieldBuilder extends FieldBuilder<Integer, IntegerListEntry> {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public IntFieldBuilder removeMin() {
     public IntFieldBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public IntFieldBuilder removeMax() {
     public IntFieldBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public IntegerListEntry build() {
     public IntegerListEntry build() {
         IntegerListEntry entry = new IntegerListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         IntegerListEntry entry = new IntegerListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -97,4 +99,4 @@ public class IntFieldBuilder extends FieldBuilder<Integer, IntegerListEntry> {
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 34 - 43
src/main/java/me/shedaniel/clothconfig2/impl/builders/IntListBuilder.java

@@ -1,8 +1,8 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
-import me.shedaniel.clothconfig2.gui.entries.BaseListEntry;
 import me.shedaniel.clothconfig2.gui.entries.IntegerListListEntry;
 import me.shedaniel.clothconfig2.gui.entries.IntegerListListEntry;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
@@ -11,139 +11,130 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class IntListBuilder extends FieldBuilder<List<Integer>, IntegerListListEntry> {
 public class IntListBuilder extends FieldBuilder<List<Integer>, IntegerListListEntry> {
-    
+
     protected Function<Integer, Optional<String>> cellErrorSupplier;
     protected Function<Integer, Optional<String>> cellErrorSupplier;
     private Consumer<List<Integer>> saveConsumer = null;
     private Consumer<List<Integer>> saveConsumer = null;
     private Function<List<Integer>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<Integer>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private List<Integer> value;
     private List<Integer> value;
-    private boolean expended = false;
+    private boolean expanded = false;
     private Integer min = null, max = null;
     private Integer min = null, max = null;
-    private Function<BaseListEntry, IntegerListListEntry.IntegerListCell> createNewInstance;
+    private Function<IntegerListListEntry, IntegerListListEntry.IntegerListCell> createNewInstance;
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private boolean deleteButtonEnabled = true, insertInFront = true;
     private boolean deleteButtonEnabled = true, insertInFront = true;
-    
+
     public IntListBuilder(String resetButtonKey, String fieldNameKey, List<Integer> value) {
     public IntListBuilder(String resetButtonKey, String fieldNameKey, List<Integer> value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public Function<Integer, Optional<String>> getCellErrorSupplier() {
     public Function<Integer, Optional<String>> getCellErrorSupplier() {
         return cellErrorSupplier;
         return cellErrorSupplier;
     }
     }
-    
+
     public IntListBuilder setCellErrorSupplier(Function<Integer, Optional<String>> cellErrorSupplier) {
     public IntListBuilder setCellErrorSupplier(Function<Integer, Optional<String>> cellErrorSupplier) {
         this.cellErrorSupplier = cellErrorSupplier;
         this.cellErrorSupplier = cellErrorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setErrorSupplier(Function<List<Integer>, Optional<String>> errorSupplier) {
     public IntListBuilder setErrorSupplier(Function<List<Integer>, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public IntListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setInsertInFront(boolean insertInFront) {
     public IntListBuilder setInsertInFront(boolean insertInFront) {
         this.insertInFront = insertInFront;
         this.insertInFront = insertInFront;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setAddButtonTooltip(String addTooltip) {
     public IntListBuilder setAddButtonTooltip(String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setRemoveButtonTooltip(String removeTooltip) {
     public IntListBuilder setRemoveButtonTooltip(String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder requireRestart() {
     public IntListBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
-    public IntListBuilder setCreateNewInstance(Function<BaseListEntry, IntegerListListEntry.IntegerListCell> createNewInstance) {
+
+    public IntListBuilder setCreateNewInstance(Function<IntegerListListEntry, IntegerListListEntry.IntegerListCell> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         return this;
         return this;
     }
     }
-    
-    public IntListBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public IntListBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setSaveConsumer(Consumer<List<Integer>> saveConsumer) {
     public IntListBuilder setSaveConsumer(Consumer<List<Integer>> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setDefaultValue(Supplier<List<Integer>> defaultValue) {
     public IntListBuilder setDefaultValue(Supplier<List<Integer>> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setMin(int min) {
     public IntListBuilder setMin(int min) {
         this.min = min;
         this.min = min;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setMax(int max) {
     public IntListBuilder setMax(int max) {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder removeMin() {
     public IntListBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder removeMax() {
     public IntListBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setDefaultValue(List<Integer> defaultValue) {
     public IntListBuilder setDefaultValue(List<Integer> defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setTooltipSupplier(Function<List<Integer>, Optional<String[]>> tooltipSupplier) {
     public IntListBuilder setTooltipSupplier(Function<List<Integer>, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
     public IntListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = list -> tooltipSupplier.get();
         this.tooltipSupplier = list -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setTooltip(Optional<String[]> tooltip) {
     public IntListBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public IntListBuilder setTooltip(String... tooltip) {
     public IntListBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public IntegerListListEntry build() {
     public IntegerListListEntry build() {
-        IntegerListListEntry entry = new IntegerListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart()) {
-            @Override
-            public boolean isDeleteButtonEnabled() {
-                return deleteButtonEnabled;
-            }
-            
-            @Override
-            public boolean insertInFront() {
-                return insertInFront;
-            }
-        };
+        IntegerListListEntry entry = new IntegerListListEntry(getFieldNameKey(), value, expanded, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart(), deleteButtonEnabled, insertInFront);
         if (min != null)
         if (min != null)
             entry.setMinimum(min);
             entry.setMinimum(min);
         if (max != null)
         if (max != null)
@@ -158,5 +149,5 @@ public class IntListBuilder extends FieldBuilder<List<Integer>, IntegerListListE
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/IntSliderBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry;
 import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -70,17 +71,18 @@ public class IntSliderBuilder extends FieldBuilder<Integer, IntegerSliderEntry>
         this.tooltipSupplier = i -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = i -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     public IntSliderBuilder setMax(int max) {
     public IntSliderBuilder setMax(int max) {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public IntSliderBuilder setMin(int min) {
     public IntSliderBuilder setMin(int min) {
         this.min = min;
         this.min = min;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public IntegerSliderEntry build() {
     public IntegerSliderEntry build() {
         IntegerSliderEntry entry = new IntegerSliderEntry(getFieldNameKey(), min, max, value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         IntegerSliderEntry entry = new IntegerSliderEntry(getFieldNameKey(), min, max, value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -92,4 +94,4 @@ public class IntSliderBuilder extends FieldBuilder<Integer, IntegerSliderEntry>
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 22 - 19
src/main/java/me/shedaniel/clothconfig2/impl/builders/KeyCodeBuilder.java

@@ -4,26 +4,28 @@ import me.shedaniel.clothconfig2.api.Modifier;
 import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.clothconfig2.gui.entries.KeyCodeEntry;
 import me.shedaniel.clothconfig2.gui.entries.KeyCodeEntry;
 import net.minecraft.client.util.InputUtil;
 import net.minecraft.client.util.InputUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class KeyCodeBuilder extends FieldBuilder<ModifierKeyCode, KeyCodeEntry> {
 public class KeyCodeBuilder extends FieldBuilder<ModifierKeyCode, KeyCodeEntry> {
-    
-    @Nullable private Consumer<ModifierKeyCode> saveConsumer = null;
-    @Nonnull private Function<ModifierKeyCode, Optional<String[]>> tooltipSupplier = bool -> Optional.empty();
+
+    @Nullable
+    private Consumer<ModifierKeyCode> saveConsumer = null;
+    @NotNull
+    private Function<ModifierKeyCode, Optional<String[]>> tooltipSupplier = bool -> Optional.empty();
     private ModifierKeyCode value;
     private ModifierKeyCode value;
     private boolean allowKey = true, allowMouse = true, allowModifiers = true;
     private boolean allowKey = true, allowMouse = true, allowModifiers = true;
-    
+
     public KeyCodeBuilder(String resetButtonKey, String fieldNameKey, ModifierKeyCode value) {
     public KeyCodeBuilder(String resetButtonKey, String fieldNameKey, ModifierKeyCode value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = ModifierKeyCode.copyOf(value);
         this.value = ModifierKeyCode.copyOf(value);
     }
     }
-    
+
     public KeyCodeBuilder setAllowModifiers(boolean allowModifiers) {
     public KeyCodeBuilder setAllowModifiers(boolean allowModifiers) {
         this.allowModifiers = allowModifiers;
         this.allowModifiers = allowModifiers;
         if (!allowModifiers)
         if (!allowModifiers)
@@ -76,40 +78,41 @@ public class KeyCodeBuilder extends FieldBuilder<ModifierKeyCode, KeyCodeEntry>
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public KeyCodeBuilder setDefaultValue(InputUtil.KeyCode defaultValue) {
     public KeyCodeBuilder setDefaultValue(InputUtil.KeyCode defaultValue) {
         return setDefaultValue(ModifierKeyCode.of(defaultValue, Modifier.none()));
         return setDefaultValue(ModifierKeyCode.of(defaultValue, Modifier.none()));
     }
     }
-    
+
     public KeyCodeBuilder setDefaultValue(ModifierKeyCode defaultValue) {
     public KeyCodeBuilder setDefaultValue(ModifierKeyCode defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
-    public KeyCodeBuilder setTooltipSupplier(@Nonnull Function<InputUtil.KeyCode, Optional<String[]>> tooltipSupplier) {
+
+    public KeyCodeBuilder setTooltipSupplier(@NotNull Function<InputUtil.KeyCode, Optional<String[]>> tooltipSupplier) {
         return setModifierTooltipSupplier(keyCode -> tooltipSupplier.apply(keyCode.getKeyCode()));
         return setModifierTooltipSupplier(keyCode -> tooltipSupplier.apply(keyCode.getKeyCode()));
     }
     }
-    
-    public KeyCodeBuilder setModifierTooltipSupplier(@Nonnull Function<ModifierKeyCode, Optional<String[]>> tooltipSupplier) {
+
+    public KeyCodeBuilder setModifierTooltipSupplier(@NotNull Function<ModifierKeyCode, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
-    public KeyCodeBuilder setTooltipSupplier(@Nonnull Supplier<Optional<String[]>> tooltipSupplier) {
+
+    public KeyCodeBuilder setTooltipSupplier(@NotNull Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = bool -> tooltipSupplier.get();
         this.tooltipSupplier = bool -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public KeyCodeBuilder setTooltip(Optional<String[]> tooltip) {
     public KeyCodeBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = bool -> tooltip;
         this.tooltipSupplier = bool -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public KeyCodeBuilder setTooltip(@Nullable String... tooltip) {
     public KeyCodeBuilder setTooltip(@Nullable String... tooltip) {
         this.tooltipSupplier = bool -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = bool -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public KeyCodeEntry build() {
     public KeyCodeEntry build() {
         KeyCodeEntry entry = new KeyCodeEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         KeyCodeEntry entry = new KeyCodeEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -122,4 +125,4 @@ public class KeyCodeBuilder extends FieldBuilder<ModifierKeyCode, KeyCodeEntry>
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/LongFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.LongListEntry;
 import me.shedaniel.clothconfig2.gui.entries.LongListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -73,17 +74,18 @@ public class LongFieldBuilder extends FieldBuilder<Long, LongListEntry> {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public LongFieldBuilder removeMin() {
     public LongFieldBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public LongFieldBuilder removeMax() {
     public LongFieldBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public LongListEntry build() {
     public LongListEntry build() {
         LongListEntry entry = new LongListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         LongListEntry entry = new LongListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -97,4 +99,4 @@ public class LongFieldBuilder extends FieldBuilder<Long, LongListEntry> {
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 34 - 43
src/main/java/me/shedaniel/clothconfig2/impl/builders/LongListBuilder.java

@@ -1,8 +1,8 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
-import me.shedaniel.clothconfig2.gui.entries.BaseListEntry;
 import me.shedaniel.clothconfig2.gui.entries.LongListListEntry;
 import me.shedaniel.clothconfig2.gui.entries.LongListListEntry;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
@@ -11,139 +11,130 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class LongListBuilder extends FieldBuilder<List<Long>, LongListListEntry> {
 public class LongListBuilder extends FieldBuilder<List<Long>, LongListListEntry> {
-    
+
     protected Function<Long, Optional<String>> cellErrorSupplier;
     protected Function<Long, Optional<String>> cellErrorSupplier;
     private Consumer<List<Long>> saveConsumer = null;
     private Consumer<List<Long>> saveConsumer = null;
     private Function<List<Long>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<Long>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private List<Long> value;
     private List<Long> value;
-    private boolean expended = false;
+    private boolean expanded = false;
     private Long min = null, max = null;
     private Long min = null, max = null;
-    private Function<BaseListEntry, LongListListEntry.LongListCell> createNewInstance;
+    private Function<LongListListEntry, LongListListEntry.LongListCell> createNewInstance;
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private boolean deleteButtonEnabled = true, insertInFront = true;
     private boolean deleteButtonEnabled = true, insertInFront = true;
-    
+
     public LongListBuilder(String resetButtonKey, String fieldNameKey, List<Long> value) {
     public LongListBuilder(String resetButtonKey, String fieldNameKey, List<Long> value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public Function<Long, Optional<String>> getCellErrorSupplier() {
     public Function<Long, Optional<String>> getCellErrorSupplier() {
         return cellErrorSupplier;
         return cellErrorSupplier;
     }
     }
-    
+
     public LongListBuilder setCellErrorSupplier(Function<Long, Optional<String>> cellErrorSupplier) {
     public LongListBuilder setCellErrorSupplier(Function<Long, Optional<String>> cellErrorSupplier) {
         this.cellErrorSupplier = cellErrorSupplier;
         this.cellErrorSupplier = cellErrorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setErrorSupplier(Function<List<Long>, Optional<String>> errorSupplier) {
     public LongListBuilder setErrorSupplier(Function<List<Long>, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public LongListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setInsertInFront(boolean insertInFront) {
     public LongListBuilder setInsertInFront(boolean insertInFront) {
         this.insertInFront = insertInFront;
         this.insertInFront = insertInFront;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setAddButtonTooltip(String addTooltip) {
     public LongListBuilder setAddButtonTooltip(String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setRemoveButtonTooltip(String removeTooltip) {
     public LongListBuilder setRemoveButtonTooltip(String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder requireRestart() {
     public LongListBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
-    public LongListBuilder setCreateNewInstance(Function<BaseListEntry, LongListListEntry.LongListCell> createNewInstance) {
+
+    public LongListBuilder setCreateNewInstance(Function<LongListListEntry, LongListListEntry.LongListCell> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         return this;
         return this;
     }
     }
-    
-    public LongListBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public LongListBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setSaveConsumer(Consumer<List<Long>> saveConsumer) {
     public LongListBuilder setSaveConsumer(Consumer<List<Long>> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setDefaultValue(Supplier<List<Long>> defaultValue) {
     public LongListBuilder setDefaultValue(Supplier<List<Long>> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setMin(long min) {
     public LongListBuilder setMin(long min) {
         this.min = min;
         this.min = min;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setMax(long max) {
     public LongListBuilder setMax(long max) {
         this.max = max;
         this.max = max;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder removeMin() {
     public LongListBuilder removeMin() {
         this.min = null;
         this.min = null;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder removeMax() {
     public LongListBuilder removeMax() {
         this.max = null;
         this.max = null;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setDefaultValue(List<Long> defaultValue) {
     public LongListBuilder setDefaultValue(List<Long> defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
     public LongListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = list -> tooltipSupplier.get();
         this.tooltipSupplier = list -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setTooltipSupplier(Function<List<Long>, Optional<String[]>> tooltipSupplier) {
     public LongListBuilder setTooltipSupplier(Function<List<Long>, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setTooltip(Optional<String[]> tooltip) {
     public LongListBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public LongListBuilder setTooltip(String... tooltip) {
     public LongListBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public LongListListEntry build() {
     public LongListListEntry build() {
-        LongListListEntry entry = new LongListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart()) {
-            @Override
-            public boolean isDeleteButtonEnabled() {
-                return deleteButtonEnabled;
-            }
-            
-            @Override
-            public boolean insertInFront() {
-                return insertInFront;
-            }
-        };
+        LongListListEntry entry = new LongListListEntry(getFieldNameKey(), value, expanded, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart(), deleteButtonEnabled, insertInFront);
         if (min != null)
         if (min != null)
             entry.setMinimum(min);
             entry.setMinimum(min);
         if (max != null)
         if (max != null)
@@ -158,5 +149,5 @@ public class LongListBuilder extends FieldBuilder<List<Long>, LongListListEntry>
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 7 - 5
src/main/java/me/shedaniel/clothconfig2/impl/builders/LongSliderBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.LongSliderEntry;
 import me.shedaniel.clothconfig2.gui.entries.LongSliderEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
@@ -60,18 +61,19 @@ public class LongSliderBuilder extends FieldBuilder<Long, LongSliderEntry> {
         this.tooltipSupplier = i -> tooltipSupplier.get();
         this.tooltipSupplier = i -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public LongSliderBuilder setTooltip(Optional<String[]> tooltip) {
     public LongSliderBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = i -> tooltip;
         this.tooltipSupplier = i -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public LongSliderBuilder setTooltip(String... tooltip) {
     public LongSliderBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = i -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = i -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
-    
+
+
+    @NotNull
     @Override
     @Override
     public LongSliderEntry build() {
     public LongSliderEntry build() {
         LongSliderEntry entry = new LongSliderEntry(getFieldNameKey(), min, max, value, saveConsumer, getResetButtonKey(), defaultValue, null, isRequireRestart());
         LongSliderEntry entry = new LongSliderEntry(getFieldNameKey(), min, max, value, saveConsumer, getResetButtonKey(), defaultValue, null, isRequireRestart());
@@ -83,4 +85,4 @@ public class LongSliderBuilder extends FieldBuilder<Long, LongSliderEntry> {
         return entry;
         return entry;
     }
     }
     
     
-}
+}

+ 6 - 4
src/main/java/me/shedaniel/clothconfig2/impl/builders/SelectorBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry;
 import me.shedaniel.clothconfig2.gui.entries.SelectionListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
@@ -63,17 +64,18 @@ public class SelectorBuilder<T> extends FieldBuilder<T, SelectionListEntry<T>> {
         this.tooltipSupplier = e -> tooltip;
         this.tooltipSupplier = e -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public SelectorBuilder<T> setTooltip(String... tooltip) {
     public SelectorBuilder<T> setTooltip(String... tooltip) {
         this.tooltipSupplier = e -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = e -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     public SelectorBuilder<T> setNameProvider(Function<T, String> enumNameProvider) {
     public SelectorBuilder<T> setNameProvider(Function<T, String> enumNameProvider) {
         this.nameProvider = enumNameProvider;
         this.nameProvider = enumNameProvider;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public SelectionListEntry<T> build() {
     public SelectionListEntry<T> build() {
         SelectionListEntry<T> entry = new SelectionListEntry<>(getFieldNameKey(), valuesArray, value, getResetButtonKey(), defaultValue, saveConsumer, nameProvider, null, isRequireRestart());
         SelectionListEntry<T> entry = new SelectionListEntry<>(getFieldNameKey(), valuesArray, value, getResetButtonKey(), defaultValue, saveConsumer, nameProvider, null, isRequireRestart());
@@ -82,5 +84,5 @@ public class SelectorBuilder<T> extends FieldBuilder<T, SelectionListEntry<T>> {
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
+
 }
 }

+ 7 - 5
src/main/java/me/shedaniel/clothconfig2/impl/builders/StringFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.StringListEntry;
 import me.shedaniel.clothconfig2.gui.entries.StringListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
@@ -54,17 +55,18 @@ public class StringFieldBuilder extends FieldBuilder<String, StringListEntry> {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public StringFieldBuilder setTooltip(Optional<String[]> tooltip) {
     public StringFieldBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = str -> tooltip;
         this.tooltipSupplier = str -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public StringFieldBuilder setTooltip(String... tooltip) {
     public StringFieldBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public StringListEntry build() {
     public StringListEntry build() {
         StringListEntry entry = new StringListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         StringListEntry entry = new StringListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -73,5 +75,5 @@ public class StringFieldBuilder extends FieldBuilder<String, StringListEntry> {
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 30 - 29
src/main/java/me/shedaniel/clothconfig2/impl/builders/StringListBuilder.java

@@ -1,8 +1,8 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
-import me.shedaniel.clothconfig2.gui.entries.BaseListEntry;
 import me.shedaniel.clothconfig2.gui.entries.StringListListEntry;
 import me.shedaniel.clothconfig2.gui.entries.StringListListEntry;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.resource.language.I18n;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
@@ -11,108 +11,109 @@ import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class StringListBuilder extends FieldBuilder<List<String>, StringListListEntry> {
 public class StringListBuilder extends FieldBuilder<List<String>, StringListListEntry> {
-    
+
     private Function<String, Optional<String>> cellErrorSupplier;
     private Function<String, Optional<String>> cellErrorSupplier;
     private Consumer<List<String>> saveConsumer = null;
     private Consumer<List<String>> saveConsumer = null;
     private Function<List<String>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<String>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private List<String> value;
     private List<String> value;
-    private boolean expended = false;
-    private Function<BaseListEntry, StringListListEntry.StringListCell> createNewInstance;
+    private boolean expanded = false;
+    private Function<StringListListEntry, StringListListEntry.StringListCell> createNewInstance;
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private String addTooltip = I18n.translate("text.cloth-config.list.add"), removeTooltip = I18n.translate("text.cloth-config.list.remove");
     private boolean deleteButtonEnabled = true, insertInFront = true;
     private boolean deleteButtonEnabled = true, insertInFront = true;
-    
+
     public StringListBuilder(String resetButtonKey, String fieldNameKey, List<String> value) {
     public StringListBuilder(String resetButtonKey, String fieldNameKey, List<String> value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     public Function<String, Optional<String>> getCellErrorSupplier() {
     public Function<String, Optional<String>> getCellErrorSupplier() {
         return cellErrorSupplier;
         return cellErrorSupplier;
     }
     }
-    
+
     public StringListBuilder setCellErrorSupplier(Function<String, Optional<String>> cellErrorSupplier) {
     public StringListBuilder setCellErrorSupplier(Function<String, Optional<String>> cellErrorSupplier) {
         this.cellErrorSupplier = cellErrorSupplier;
         this.cellErrorSupplier = cellErrorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setErrorSupplier(Function<List<String>, Optional<String>> errorSupplier) {
     public StringListBuilder setErrorSupplier(Function<List<String>, Optional<String>> errorSupplier) {
         this.errorSupplier = errorSupplier;
         this.errorSupplier = errorSupplier;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public StringListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setInsertInFront(boolean insertInFront) {
     public StringListBuilder setInsertInFront(boolean insertInFront) {
         this.insertInFront = insertInFront;
         this.insertInFront = insertInFront;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setAddButtonTooltip(String addTooltip) {
     public StringListBuilder setAddButtonTooltip(String addTooltip) {
         this.addTooltip = addTooltip;
         this.addTooltip = addTooltip;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setRemoveButtonTooltip(String removeTooltip) {
     public StringListBuilder setRemoveButtonTooltip(String removeTooltip) {
         this.removeTooltip = removeTooltip;
         this.removeTooltip = removeTooltip;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder requireRestart() {
     public StringListBuilder requireRestart() {
         requireRestart(true);
         requireRestart(true);
         return this;
         return this;
     }
     }
-    
-    public StringListBuilder setCreateNewInstance(Function<BaseListEntry, StringListListEntry.StringListCell> createNewInstance) {
+
+    public StringListBuilder setCreateNewInstance(Function<StringListListEntry, StringListListEntry.StringListCell> createNewInstance) {
         this.createNewInstance = createNewInstance;
         this.createNewInstance = createNewInstance;
         return this;
         return this;
     }
     }
-    
-    public StringListBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public StringListBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setSaveConsumer(Consumer<List<String>> saveConsumer) {
     public StringListBuilder setSaveConsumer(Consumer<List<String>> saveConsumer) {
         this.saveConsumer = saveConsumer;
         this.saveConsumer = saveConsumer;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setDefaultValue(Supplier<List<String>> defaultValue) {
     public StringListBuilder setDefaultValue(Supplier<List<String>> defaultValue) {
         this.defaultValue = defaultValue;
         this.defaultValue = defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setDefaultValue(List<String> defaultValue) {
     public StringListBuilder setDefaultValue(List<String> defaultValue) {
         this.defaultValue = () -> defaultValue;
         this.defaultValue = () -> defaultValue;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
     public StringListBuilder setTooltipSupplier(Supplier<Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = list -> tooltipSupplier.get();
         this.tooltipSupplier = list -> tooltipSupplier.get();
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setTooltipSupplier(Function<List<String>, Optional<String[]>> tooltipSupplier) {
     public StringListBuilder setTooltipSupplier(Function<List<String>, Optional<String[]>> tooltipSupplier) {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setTooltip(Optional<String[]> tooltip) {
     public StringListBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public StringListBuilder setTooltip(String... tooltip) {
     public StringListBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public StringListListEntry build() {
     public StringListListEntry build() {
-        StringListListEntry entry = new StringListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart());
+        StringListListEntry entry = new StringListListEntry(getFieldNameKey(), value, expanded, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart(), deleteButtonEnabled, insertInFront);
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
         entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setCellErrorSupplier(cellErrorSupplier);
@@ -123,5 +124,5 @@ public class StringListBuilder extends FieldBuilder<List<String>, StringListList
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}

+ 12 - 10
src/main/java/me/shedaniel/clothconfig2/impl/builders/SubCategoryBuilder.java

@@ -3,6 +3,7 @@ package me.shedaniel.clothconfig2.impl.builders;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.gui.entries.SubCategoryListEntry;
 import me.shedaniel.clothconfig2.gui.entries.SubCategoryListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.*;
 import java.util.*;
 import java.util.function.Function;
 import java.util.function.Function;
@@ -12,7 +13,7 @@ public class SubCategoryBuilder extends FieldBuilder<Object, SubCategoryListEntr
     
     
     private List<AbstractConfigListEntry> entries;
     private List<AbstractConfigListEntry> entries;
     private Function<List<AbstractConfigListEntry>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
     private Function<List<AbstractConfigListEntry>, Optional<String[]>> tooltipSupplier = list -> Optional.empty();
-    private boolean expended = false;
+    private boolean expanded = false;
     
     
     public SubCategoryBuilder(String resetButtonKey, String fieldNameKey) {
     public SubCategoryBuilder(String resetButtonKey, String fieldNameKey) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
@@ -33,29 +34,30 @@ public class SubCategoryBuilder extends FieldBuilder<Object, SubCategoryListEntr
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public SubCategoryBuilder setTooltip(Optional<String[]> tooltip) {
     public SubCategoryBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = list -> tooltip;
         this.tooltipSupplier = list -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public SubCategoryBuilder setTooltip(String... tooltip) {
     public SubCategoryBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = list -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
-    public SubCategoryBuilder setExpended(boolean expended) {
-        this.expended = expended;
+
+    public SubCategoryBuilder setExpanded(boolean expanded) {
+        this.expanded = expanded;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public SubCategoryListEntry build() {
     public SubCategoryListEntry build() {
-        SubCategoryListEntry entry = new SubCategoryListEntry(getFieldNameKey(), entries, expended);
+        SubCategoryListEntry entry = new SubCategoryListEntry(getFieldNameKey(), entries, expanded);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
+
     @Override
     @Override
     public int size() {
     public int size() {
         return entries.size();
         return entries.size();
@@ -171,4 +173,4 @@ public class SubCategoryBuilder extends FieldBuilder<Object, SubCategoryListEntr
         return entries.subList(fromIndex, toIndex);
         return entries.subList(fromIndex, toIndex);
     }
     }
     
     
-}
+}

+ 13 - 10
src/main/java/me/shedaniel/clothconfig2/impl/builders/TextDescriptionBuilder.java

@@ -1,22 +1,24 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.TextListEntry;
 import me.shedaniel.clothconfig2.gui.entries.TextListEntry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 
-import javax.annotation.Nullable;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public class TextDescriptionBuilder extends FieldBuilder<String, TextListEntry> {
 public class TextDescriptionBuilder extends FieldBuilder<String, TextListEntry> {
-    
+
     private int color = -1;
     private int color = -1;
-    @Nullable private Supplier<Optional<String[]>> tooltipSupplier = null;
+    @Nullable
+    private Supplier<Optional<String[]>> tooltipSupplier = null;
     private String value;
     private String value;
-    
+
     public TextDescriptionBuilder(String resetButtonKey, String fieldNameKey, String value) {
     public TextDescriptionBuilder(String resetButtonKey, String fieldNameKey, String value) {
         super(resetButtonKey, fieldNameKey);
         super(resetButtonKey, fieldNameKey);
         this.value = value;
         this.value = value;
     }
     }
-    
+
     @Override
     @Override
     public void requireRestart(boolean requireRestart) {
     public void requireRestart(boolean requireRestart) {
         throw new UnsupportedOperationException();
         throw new UnsupportedOperationException();
@@ -31,20 +33,21 @@ public class TextDescriptionBuilder extends FieldBuilder<String, TextListEntry>
         this.tooltipSupplier = () -> tooltip;
         this.tooltipSupplier = () -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public TextDescriptionBuilder setTooltip(String... tooltip) {
     public TextDescriptionBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = () -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = () -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
     public TextDescriptionBuilder setColor(int color) {
     public TextDescriptionBuilder setColor(int color) {
         this.color = color;
         this.color = color;
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public TextListEntry build() {
     public TextListEntry build() {
         return new TextListEntry(getFieldNameKey(), value, color, tooltipSupplier);
         return new TextListEntry(getFieldNameKey(), value, color, tooltipSupplier);
     }
     }
-    
-}
+
+}

+ 7 - 5
src/main/java/me/shedaniel/clothconfig2/impl/builders/TextFieldBuilder.java

@@ -1,6 +1,7 @@
 package me.shedaniel.clothconfig2.impl.builders;
 package me.shedaniel.clothconfig2.impl.builders;
 
 
 import me.shedaniel.clothconfig2.gui.entries.StringListEntry;
 import me.shedaniel.clothconfig2.gui.entries.StringListEntry;
+import org.jetbrains.annotations.NotNull;
 
 
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
@@ -54,17 +55,18 @@ public class TextFieldBuilder extends FieldBuilder<String, StringListEntry> {
         this.tooltipSupplier = tooltipSupplier;
         this.tooltipSupplier = tooltipSupplier;
         return this;
         return this;
     }
     }
-    
+
     public TextFieldBuilder setTooltip(Optional<String[]> tooltip) {
     public TextFieldBuilder setTooltip(Optional<String[]> tooltip) {
         this.tooltipSupplier = str -> tooltip;
         this.tooltipSupplier = str -> tooltip;
         return this;
         return this;
     }
     }
-    
+
     public TextFieldBuilder setTooltip(String... tooltip) {
     public TextFieldBuilder setTooltip(String... tooltip) {
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         this.tooltipSupplier = str -> Optional.ofNullable(tooltip);
         return this;
         return this;
     }
     }
-    
+
+    @NotNull
     @Override
     @Override
     public StringListEntry build() {
     public StringListEntry build() {
         StringListEntry entry = new StringListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
         StringListEntry entry = new StringListEntry(getFieldNameKey(), value, getResetButtonKey(), defaultValue, saveConsumer, null, isRequireRestart());
@@ -73,5 +75,5 @@ public class TextFieldBuilder extends FieldBuilder<String, StringListEntry> {
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
             entry.setErrorSupplier(() -> errorSupplier.apply(entry.getValue()));
         return entry;
         return entry;
     }
     }
-    
-}
+
+}