Danielshe 5 years ago
parent
commit
2077a223a5
26 changed files with 249 additions and 69 deletions
  1. 1 1
      gradle.properties
  2. 2 2
      src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java
  3. 1 1
      src/main/java/me/shedaniel/clothconfig2/api/AbstractConfigEntry.java
  4. 4 0
      src/main/java/me/shedaniel/clothconfig2/api/AbstractConfigListEntry.java
  5. 2 0
      src/main/java/me/shedaniel/clothconfig2/api/ConfigBuilder.java
  6. 2 0
      src/main/java/me/shedaniel/clothconfig2/api/ConfigCategory.java
  7. 22 2
      src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java
  8. 17 0
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListCell.java
  9. 3 3
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListEntry.java
  10. 1 1
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BooleanListEntry.java
  11. 22 12
      src/main/java/me/shedaniel/clothconfig2/gui/entries/DoubleListListEntry.java
  12. 2 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/EnumListEntry.java
  13. 21 11
      src/main/java/me/shedaniel/clothconfig2/gui/entries/FloatListListEntry.java
  14. 21 11
      src/main/java/me/shedaniel/clothconfig2/gui/entries/IntegerListListEntry.java
  15. 2 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/IntegerSliderEntry.java
  16. 21 11
      src/main/java/me/shedaniel/clothconfig2/gui/entries/LongListListEntry.java
  17. 2 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/LongSliderEntry.java
  18. 14 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/StringListListEntry.java
  19. 2 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/TextFieldListEntry.java
  20. 24 3
      src/main/java/me/shedaniel/clothconfig2/impl/ConfigBuilderImpl.java
  21. 8 1
      src/main/java/me/shedaniel/clothconfig2/impl/ConfigCategoryImpl.java
  22. 11 0
      src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleListBuilder.java
  23. 11 0
      src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatListBuilder.java
  24. 11 0
      src/main/java/me/shedaniel/clothconfig2/impl/builders/IntListBuilder.java
  25. 11 0
      src/main/java/me/shedaniel/clothconfig2/impl/builders/LongListBuilder.java
  26. 11 0
      src/main/java/me/shedaniel/clothconfig2/impl/builders/StringListBuilder.java

+ 1 - 1
gradle.properties

@@ -2,5 +2,5 @@ minecraft_version=1.14.4
 yarn_version=1.14.4+build.8
 yarn_version=1.14.4+build.8
 fabric_loader_version=0.4.8+build.159
 fabric_loader_version=0.4.8+build.159
 fabric_version=0.3.0+build.207
 fabric_version=0.3.0+build.207
-mod_version=1.0.0
+mod_version=1.0.1
 modmenu_version=1.7.6+build.115
 modmenu_version=1.7.6+build.115

+ 2 - 2
src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java

@@ -35,7 +35,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
                         playZone.addEntry(entryBuilder.startBooleanToggle("Simple Boolean", false).build());
                         playZone.addEntry(entryBuilder.startBooleanToggle("Simple Boolean", false).build());
                         playZone.addEntry(entryBuilder.startStrField("Simple String", "ab").setDefaultValue(() -> "ab").build());
                         playZone.addEntry(entryBuilder.startStrField("Simple String", "ab").setDefaultValue(() -> "ab").build());
                         playZone.addEntry(entryBuilder.startLongSlider("Long Slider", 0, -10, 10).setDefaultValue(() -> 0l).build());
                         playZone.addEntry(entryBuilder.startLongSlider("Long Slider", 0, -10, 10).setDefaultValue(() -> 0l).build());
-                        playZone.addEntry(entryBuilder.startIntList("Int List", Arrays.asList(1, 6, 14, 1414)).setTooltip("this is a bad tooltip").setSaveConsumer(integers -> integers.forEach(System.out::println)).setDefaultValue(Arrays.asList(1, 6, 14, 1414)).build());
+                        playZone.addEntry(entryBuilder.startDoubleList("Double List", Arrays.asList(1d, 6d, 14d, 1414d)).setTooltip("this is a bad tooltip").setSaveConsumer(integers -> integers.forEach(System.out::println)).setCellErrorSupplier(aDouble -> aDouble == 2 ? Optional.of("2 is not accepted!") : Optional.empty()).setDefaultValue(Arrays.asList(1d, 6d, 14d, 1414d)).build());
                         playZone.addEntry(entryBuilder.startStrList("Party Member List", Arrays.asList("Tim", "Daniel", "John")).setTooltip("A list of party members.").setDefaultValue(Arrays.asList("Tim", "Daniel", "John")).build());
                         playZone.addEntry(entryBuilder.startStrList("Party Member List", Arrays.asList("Tim", "Daniel", "John")).setTooltip("A list of party members.").setDefaultValue(Arrays.asList("Tim", "Daniel", "John")).build());
                         playZone.addEntry(entryBuilder.startIntField("Integer Field", 2).setErrorSupplier(integer -> {
                         playZone.addEntry(entryBuilder.startIntField("Integer Field", 2).setErrorSupplier(integer -> {
                             if (integer == 4)
                             if (integer == 4)
@@ -51,7 +51,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
                         ConfigCategory enumZone = builder.getOrCreateCategory("Enum Zone");
                         ConfigCategory enumZone = builder.getOrCreateCategory("Enum Zone");
                         enumZone.setCategoryBackground(new Identifier("minecraft:textures/block/stone.png"));
                         enumZone.setCategoryBackground(new Identifier("minecraft:textures/block/stone.png"));
                         enumZone.addEntry(entryBuilder.startEnumSelector("Enum Field", DemoEnum.class, DemoEnum.CONSTANT_2).setDefaultValue(() -> DemoEnum.CONSTANT_1).build());
                         enumZone.addEntry(entryBuilder.startEnumSelector("Enum Field", DemoEnum.class, DemoEnum.CONSTANT_2).setDefaultValue(() -> DemoEnum.CONSTANT_1).build());
-                        ConfigCategory partyZone = builder.getOrCreateCategory("Party Zone");
+                        builder.setFallbackCategory(enumZone);
                         MinecraftClient.getInstance().openScreen(builder.build());
                         MinecraftClient.getInstance().openScreen(builder.build());
                     } catch (Throwable throwable) {
                     } catch (Throwable throwable) {
                         throwable.printStackTrace();
                         throwable.printStackTrace();

+ 1 - 1
src/main/java/me/shedaniel/clothconfig2/api/AbstractConfigEntry.java

@@ -18,7 +18,7 @@ public abstract class AbstractConfigEntry<T> extends DynamicElementListWidget.El
     
     
     public abstract T getValue();
     public abstract T getValue();
     
     
-    public Optional<String> getConfigError() {
+    public final Optional<String> getConfigError() {
         if (errorSupplier != null && errorSupplier.get().isPresent())
         if (errorSupplier != null && errorSupplier.get().isPresent())
             return errorSupplier.get();
             return errorSupplier.get();
         return getError();
         return getError();

+ 4 - 0
src/main/java/me/shedaniel/clothconfig2/api/AbstractConfigListEntry.java

@@ -28,6 +28,10 @@ public abstract class AbstractConfigListEntry<T> extends AbstractConfigEntry<T>
         this.editable = editable;
         this.editable = editable;
     }
     }
     
     
+    public final int getPreferredTextColor() {
+        return getConfigError().isPresent() ? 16733525 : 16777215;
+    }
+    
     @Override
     @Override
     public String getFieldName() {
     public String getFieldName() {
         return fieldName;
         return fieldName;

+ 2 - 0
src/main/java/me/shedaniel/clothconfig2/api/ConfigBuilder.java

@@ -22,6 +22,8 @@ public interface ConfigBuilder {
         return create().setParentScreen(parent).setTitle(title);
         return create().setParentScreen(parent).setTitle(title);
     }
     }
     
     
+    ConfigBuilder setFallbackCategory(ConfigCategory fallbackCategory);
+    
     Screen getParentScreen();
     Screen getParentScreen();
     
     
     ConfigBuilder setParentScreen(Screen parent);
     ConfigBuilder setParentScreen(Screen parent);

+ 2 - 0
src/main/java/me/shedaniel/clothconfig2/api/ConfigCategory.java

@@ -6,6 +6,8 @@ import java.util.List;
 
 
 public interface ConfigCategory {
 public interface ConfigCategory {
     
     
+    String getCategoryKey();
+    
     @Deprecated
     @Deprecated
     List<Object> getEntries();
     List<Object> getEntries();
     
     

+ 22 - 2
src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java

@@ -99,19 +99,31 @@ public abstract class ClothConfigScreen extends Screen {
             list.forEach(entry -> entry.setScreen(this));
             list.forEach(entry -> entry.setScreen(this));
             tabbedEntries.put(tab, list);
             tabbedEntries.put(tab, list);
         });
         });
+        TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+        this.tabs = tabbedEntries.keySet().stream().map(s -> new Pair<>(s, textRenderer.getStringWidth(I18n.translate(s)) + 8)).collect(Collectors.toList());
         this.nextTabIndex = 0;
         this.nextTabIndex = 0;
         this.selectedTabIndex = 0;
         this.selectedTabIndex = 0;
+        for(int i = 0; i < tabs.size(); i++) {
+            Pair<String, Integer> pair = tabs.get(i);
+            if (pair.getLeft().equals(getFallbackCategory())) {
+                this.nextTabIndex = i;
+                this.selectedTabIndex = i;
+                break;
+            }
+        }
         this.confirmSave = confirmSave;
         this.confirmSave = confirmSave;
         this.edited = false;
         this.edited = false;
         this.requiresRestart = false;
         this.requiresRestart = false;
-        TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
-        this.tabs = tabbedEntries.keySet().stream().map(s -> new Pair<>(s, textRenderer.getStringWidth(I18n.translate(s)) + 8)).collect(Collectors.toList());
         this.tabsScrollProgress = 0d;
         this.tabsScrollProgress = 0d;
         this.tabButtons = Lists.newArrayList();
         this.tabButtons = Lists.newArrayList();
         this.displayErrors = displayErrors;
         this.displayErrors = displayErrors;
         this.categoryBackgroundLocation = categoryBackgroundLocation;
         this.categoryBackgroundLocation = categoryBackgroundLocation;
     }
     }
     
     
+    public String getFallbackCategory() {
+        return tabs.get(0).getLeft();
+    }
+    
     @Override
     @Override
     public void tick() {
     public void tick() {
         super.tick();
         super.tick();
@@ -462,6 +474,14 @@ public abstract class ClothConfigScreen extends Screen {
         protected final void clearStuff() {
         protected final void clearStuff() {
             this.clearItems();
             this.clearItems();
         }
         }
+        
+        @Override
+        public boolean mouseClicked(double double_1, double double_2, int int_1) {
+            boolean b = super.mouseClicked(double_1, double_2, int_1);
+            if (!scroller.isRegistered())
+                scroller.registerTick();
+            return b;
+        }
     }
     }
     
     
 }
 }

+ 17 - 0
src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListCell.java

@@ -3,9 +3,26 @@ package me.shedaniel.clothconfig2.gui.entries;
 import net.minecraft.client.gui.AbstractParentElement;
 import net.minecraft.client.gui.AbstractParentElement;
 
 
 import java.util.Optional;
 import java.util.Optional;
+import java.util.function.Supplier;
 
 
 public abstract class BaseListCell extends AbstractParentElement {
 public abstract class BaseListCell extends AbstractParentElement {
     
     
+    private Supplier<Optional<String>> errorSupplier;
+    
+    public final int getPreferredTextColor() {
+        return getConfigError().isPresent() ? 16733525 : 14737632;
+    }
+    
+    public final Optional<String> getConfigError() {
+        if (errorSupplier != null && errorSupplier.get().isPresent())
+            return errorSupplier.get();
+        return getError();
+    }
+    
+    public void setErrorSupplier(Supplier<Optional<String>> errorSupplier) {
+        this.errorSupplier = errorSupplier;
+    }
+    
     public abstract Optional<String> getError();
     public abstract Optional<String> getError();
     
     
     public abstract int getCellHeight();
     public abstract int getCellHeight();

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

@@ -112,10 +112,10 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
     public Optional<String> getError() {
     public Optional<String> getError() {
         String error = null;
         String error = null;
         for(BaseListCell entry : cells)
         for(BaseListCell entry : cells)
-            if (entry.getError().isPresent()) {
+            if (entry.getConfigError().isPresent()) {
                 if (error != null)
                 if (error != null)
                     return Optional.ofNullable(I18n.translate("text.cloth-config.multi_error"));
                     return Optional.ofNullable(I18n.translate("text.cloth-config.multi_error"));
-                return Optional.ofNullable((String) entry.getError().get());
+                return Optional.ofNullable((String) entry.getConfigError().get());
             }
             }
         return Optional.ofNullable(error);
         return Optional.ofNullable(error);
     }
     }
@@ -178,7 +178,7 @@ public abstract class BaseListEntry<T, C extends BaseListCell> extends TooltipLi
         resetWidget.y = y;
         resetWidget.y = y;
         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 : -1);
+        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 (expended) {
             int yy = y + 24;
             int yy = y + 24;
             for(BaseListCell cell : cells) {
             for(BaseListCell cell : cells) {

+ 1 - 1
src/main/java/me/shedaniel/clothconfig2/gui/entries/BooleanListEntry.java

@@ -84,7 +84,7 @@ public class BooleanListEntry extends TooltipListEntry<Boolean> {
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
         } else {
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.buttonWidget.x = x + entryWidth - 150;
             this.buttonWidget.x = x + entryWidth - 150;
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);

+ 22 - 12
src/main/java/me/shedaniel/clothconfig2/gui/entries/DoubleListListEntry.java

@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
 public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEntry.DoubleListCell> {
 public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEntry.DoubleListCell> {
     
     
     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) {
     public DoubleListListEntry(String fieldName, List<Double> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Double>> saveConsumer, Supplier<List<Double>> defaultValue, String resetButtonKey) {
@@ -33,9 +34,17 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
         expended = defaultExpended;
         expended = defaultExpended;
     }
     }
     
     
+    public void setCellErrorSupplier(Function<Double, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+    
+    public Function<Double, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
     @Override
     @Override
     public List<Double> getValue() {
     public List<Double> getValue() {
-        return cells.stream().map(cell -> Double.valueOf(cell.widget.getText())).collect(Collectors.toList());
+        return cells.stream().map(DoubleListCell::getValue).collect(Collectors.toList());
     }
     }
     
     
     public DoubleListListEntry setMaximum(Double maximum) {
     public DoubleListListEntry setMaximum(Double maximum) {
@@ -72,20 +81,13 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
         
         
         public DoubleListCell(double value, DoubleListListEntry listListEntry) {
         public DoubleListCell(double value, DoubleListListEntry listListEntry) {
             this.listListEntry = 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, "") {
             widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
                 @Override
                 @Override
                 public void render(int int_1, int int_2, float Double_1) {
                 public void render(int int_1, int int_2, float Double_1) {
                     boolean f = isFocused();
                     boolean f = isFocused();
                     setFocused(isSelected);
                     setFocused(isSelected);
-                    try {
-                        double i = Double.valueOf(getText());
-                        if (i < listListEntry.minimum || i > listListEntry.maximum)
-                            widget.setEditableColor(16733525);
-                        else
-                            widget.setEditableColor(14737632);
-                    } catch (NumberFormatException ex) {
-                        widget.setEditableColor(16733525);
-                    }
+                    widget.setEditableColor(getPreferredTextColor());
                     super.render(int_1, int_2, Double_1);
                     super.render(int_1, int_2, Double_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
@@ -104,6 +106,14 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
             });
             });
         }
         }
         
         
+        public double getValue() {
+            try {
+                return Double.valueOf(widget.getText());
+            } catch (NumberFormatException e) {
+                return 0d;
+            }
+        }
+        
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
@@ -113,7 +123,7 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
                 else if (i < listListEntry.minimum)
                 else if (i < listListEntry.minimum)
                     return Optional.of(I18n.translate("text.cloth-config.error.too_small", listListEntry.minimum));
                     return Optional.of(I18n.translate("text.cloth-config.error.too_small", listListEntry.minimum));
             } catch (NumberFormatException ex) {
             } catch (NumberFormatException ex) {
-                return Optional.of(I18n.translate("text.cloth-config.error.not_valid_number_Double"));
+                return Optional.of(I18n.translate("text.cloth-config.error.not_valid_number_double"));
             }
             }
             return Optional.empty();
             return Optional.empty();
         }
         }
@@ -132,7 +142,7 @@ public class DoubleListListEntry extends BaseListEntry<Double, DoubleListListEnt
             this.isSelected = isSelected;
             this.isSelected = isSelected;
             widget.render(mouseX, mouseY, delta);
             widget.render(mouseX, mouseY, delta);
             if (isSelected && listListEntry.isEditable())
             if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
         }
         }
         
         
         @Override
         @Override

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

@@ -97,12 +97,12 @@ public class EnumListEntry<T extends Enum<?>> extends TooltipListEntry<T> {
         this.buttonWidget.y = y;
         this.buttonWidget.y = y;
         this.buttonWidget.setMessage(enumNameProvider.apply(getValue()));
         this.buttonWidget.setMessage(enumNameProvider.apply(getValue()));
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.resetButton.x = x;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
         } else {
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.buttonWidget.x = x + entryWidth - 150;
             this.buttonWidget.x = x + entryWidth - 150;
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.buttonWidget.setWidth(150 - resetButton.getWidth() - 2);

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

@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
 public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.FloatListCell> {
 public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.FloatListCell> {
     
     
     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) {
     public FloatListListEntry(String fieldName, List<Float> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Float>> saveConsumer, Supplier<List<Float>> defaultValue, String resetButtonKey) {
@@ -33,9 +34,17 @@ public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.
         expended = defaultExpended;
         expended = defaultExpended;
     }
     }
     
     
+    public Function<Float, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public void setCellErrorSupplier(Function<Float, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+    
     @Override
     @Override
     public List<Float> getValue() {
     public List<Float> getValue() {
-        return cells.stream().map(cell -> Float.valueOf(cell.widget.getText())).collect(Collectors.toList());
+        return cells.stream().map(FloatListCell::getValue).collect(Collectors.toList());
     }
     }
     
     
     public FloatListListEntry setMaximum(float maximum) {
     public FloatListListEntry setMaximum(float maximum) {
@@ -72,20 +81,13 @@ public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.
         
         
         public FloatListCell(float value, FloatListListEntry listListEntry) {
         public FloatListCell(float value, FloatListListEntry listListEntry) {
             this.listListEntry = 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, "") {
             widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
                 @Override
                 @Override
                 public void render(int int_1, int int_2, float float_1) {
                 public void render(int int_1, int int_2, float float_1) {
                     boolean f = isFocused();
                     boolean f = isFocused();
                     setFocused(isSelected);
                     setFocused(isSelected);
-                    try {
-                        float i = Float.valueOf(getText());
-                        if (i < listListEntry.minimum || i > listListEntry.maximum)
-                            widget.setEditableColor(16733525);
-                        else
-                            widget.setEditableColor(14737632);
-                    } catch (NumberFormatException ex) {
-                        widget.setEditableColor(16733525);
-                    }
+                    widget.setEditableColor(getPreferredTextColor());
                     super.render(int_1, int_2, float_1);
                     super.render(int_1, int_2, float_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
@@ -104,6 +106,14 @@ public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.
             });
             });
         }
         }
         
         
+        public float getValue() {
+            try {
+                return Float.valueOf(widget.getText());
+            } catch (NumberFormatException e) {
+                return 0f;
+            }
+        }
+        
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
@@ -132,7 +142,7 @@ public class FloatListListEntry extends BaseListEntry<Float, FloatListListEntry.
             this.isSelected = isSelected;
             this.isSelected = isSelected;
             widget.render(mouseX, mouseY, delta);
             widget.render(mouseX, mouseY, delta);
             if (isSelected && listListEntry.isEditable())
             if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
         }
         }
         
         
         @Override
         @Override

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

@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
 public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListListEntry.IntegerListCell> {
 public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListListEntry.IntegerListCell> {
     
     
     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) {
     public IntegerListListEntry(String fieldName, List<Integer> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Integer>> saveConsumer, Supplier<List<Integer>> defaultValue, String resetButtonKey) {
@@ -33,9 +34,17 @@ public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListList
         expended = defaultExpended;
         expended = defaultExpended;
     }
     }
     
     
+    public Function<Integer, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public void setCellErrorSupplier(Function<Integer, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+    
     @Override
     @Override
     public List<Integer> getValue() {
     public List<Integer> getValue() {
-        return cells.stream().map(cell -> Integer.valueOf(cell.widget.getText())).collect(Collectors.toList());
+        return cells.stream().map(IntegerListCell::getValue).collect(Collectors.toList());
     }
     }
     
     
     public IntegerListListEntry setMaximum(int maximum) {
     public IntegerListListEntry setMaximum(int maximum) {
@@ -72,20 +81,13 @@ public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListList
         
         
         public IntegerListCell(int value, IntegerListListEntry listListEntry) {
         public IntegerListCell(int value, IntegerListListEntry listListEntry) {
             this.listListEntry = 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, "") {
             widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
                 @Override
                 @Override
                 public void render(int int_1, int int_2, float float_1) {
                 public void render(int int_1, int int_2, float float_1) {
                     boolean f = isFocused();
                     boolean f = isFocused();
                     setFocused(isSelected);
                     setFocused(isSelected);
-                    try {
-                        int i = Integer.valueOf(getText());
-                        if (i < listListEntry.minimum || i > listListEntry.maximum)
-                            widget.setEditableColor(16733525);
-                        else
-                            widget.setEditableColor(14737632);
-                    } catch (NumberFormatException ex) {
-                        widget.setEditableColor(16733525);
-                    }
+                    widget.setEditableColor(getPreferredTextColor());
                     super.render(int_1, int_2, float_1);
                     super.render(int_1, int_2, float_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
@@ -104,6 +106,14 @@ public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListList
             });
             });
         }
         }
         
         
+        public int getValue() {
+            try {
+                return Integer.valueOf(widget.getText());
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+        
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
@@ -132,7 +142,7 @@ public class IntegerListListEntry extends BaseListEntry<Integer, IntegerListList
             this.isSelected = isSelected;
             this.isSelected = isSelected;
             widget.render(mouseX, mouseY, delta);
             widget.render(mouseX, mouseY, delta);
             if (isSelected && listListEntry.isEditable())
             if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
         }
         }
         
         
         @Override
         @Override

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

@@ -111,12 +111,12 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         this.sliderWidget.active = isEditable();
         this.sliderWidget.active = isEditable();
         this.sliderWidget.y = y;
         this.sliderWidget.y = y;
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.resetButton.x = x;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
         } else {
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.sliderWidget.x = x + entryWidth - 150;
             this.sliderWidget.x = x + entryWidth - 150;
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);

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

@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
 public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.LongListCell> {
 public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.LongListCell> {
     
     
     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) {
     public LongListListEntry(String fieldName, List<Long> value, boolean defaultExpended, Supplier<Optional<String[]>> tooltipSupplier, Consumer<List<Long>> saveConsumer, Supplier<List<Long>> defaultValue, String resetButtonKey) {
@@ -33,9 +34,17 @@ public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.Lon
         expended = defaultExpended;
         expended = defaultExpended;
     }
     }
     
     
+    public Function<Long, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public void setCellErrorSupplier(Function<Long, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+    
     @Override
     @Override
     public List<Long> getValue() {
     public List<Long> getValue() {
-        return cells.stream().map(cell -> Long.valueOf(cell.widget.getText())).collect(Collectors.toList());
+        return cells.stream().map(LongListCell::getValue).collect(Collectors.toList());
     }
     }
     
     
     public LongListListEntry setMaximum(long maximum) {
     public LongListListEntry setMaximum(long maximum) {
@@ -72,20 +81,13 @@ public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.Lon
         
         
         public LongListCell(long value, LongListListEntry listListEntry) {
         public LongListCell(long value, LongListListEntry listListEntry) {
             this.listListEntry = 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, "") {
             widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
                 @Override
                 @Override
                 public void render(int int_1, int int_2, float float_1) {
                 public void render(int int_1, int int_2, float float_1) {
                     boolean f = isFocused();
                     boolean f = isFocused();
                     setFocused(isSelected);
                     setFocused(isSelected);
-                    try {
-                        long l = Long.valueOf(getText());
-                        if (l < listListEntry.minimum || l > listListEntry.maximum)
-                            widget.setEditableColor(16733525);
-                        else
-                            widget.setEditableColor(14737632);
-                    } catch (NumberFormatException ex) {
-                        widget.setEditableColor(16733525);
-                    }
+                    widget.setEditableColor(getPreferredTextColor());
                     super.render(int_1, int_2, float_1);
                     super.render(int_1, int_2, float_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
@@ -104,6 +106,14 @@ public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.Lon
             });
             });
         }
         }
         
         
+        public long getValue() {
+            try {
+                return Long.valueOf(widget.getText());
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+        
         @Override
         @Override
         public Optional<String> getError() {
         public Optional<String> getError() {
             try {
             try {
@@ -132,7 +142,7 @@ public class LongListListEntry extends BaseListEntry<Long, LongListListEntry.Lon
             this.isSelected = isSelected;
             this.isSelected = isSelected;
             widget.render(mouseX, mouseY, delta);
             widget.render(mouseX, mouseY, delta);
             if (isSelected && listListEntry.isEditable())
             if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
         }
         }
         
         
         @Override
         @Override

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

@@ -111,12 +111,12 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
         this.sliderWidget.active = isEditable();
         this.sliderWidget.active = isEditable();
         this.sliderWidget.y = y;
         this.sliderWidget.y = y;
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.resetButton.x = x;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
         } else {
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.sliderWidget.x = x + entryWidth - 150;
             this.sliderWidget.x = x + entryWidth - 150;
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);
             this.sliderWidget.setWidth(150 - resetButton.getWidth() - 2);

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

@@ -8,11 +8,14 @@ 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;
 import java.util.stream.Collectors;
 
 
 public class StringListListEntry extends BaseListEntry<String, StringListListEntry.StringListCell> {
 public class StringListListEntry extends BaseListEntry<String, StringListListEntry.StringListCell> {
     
     
+    private Function<String, Optional<String>> cellErrorSupplier;
+    
     @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) {
     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);
         this(fieldName, value, defaultExpended, tooltipSupplier, saveConsumer, defaultValue, resetButtonKey, false);
@@ -27,6 +30,14 @@ public class StringListListEntry extends BaseListEntry<String, StringListListEnt
         expended = defaultExpended;
         expended = defaultExpended;
     }
     }
     
     
+    public Function<String, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public void setCellErrorSupplier(Function<String, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+    }
+    
     @Override
     @Override
     public List<String> getValue() {
     public List<String> getValue() {
         return cells.stream().map(cell -> cell.widget.getText()).collect(Collectors.toList());
         return cells.stream().map(cell -> cell.widget.getText()).collect(Collectors.toList());
@@ -45,12 +56,13 @@ public class StringListListEntry extends BaseListEntry<String, StringListListEnt
         
         
         public StringListCell(String value, StringListListEntry listListEntry) {
         public StringListCell(String value, StringListListEntry listListEntry) {
             this.listListEntry = 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, "") {
             widget = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, 0, 0, 100, 18, "") {
                 @Override
                 @Override
                 public void render(int int_1, int int_2, float float_1) {
                 public void render(int int_1, int int_2, float float_1) {
                     boolean f = isFocused();
                     boolean f = isFocused();
                     setFocused(isSelected);
                     setFocused(isSelected);
-                    widget.setEditableColor(14737632);
+                    widget.setEditableColor(getPreferredTextColor());
                     super.render(int_1, int_2, float_1);
                     super.render(int_1, int_2, float_1);
                     setFocused(f);
                     setFocused(f);
                 }
                 }
@@ -83,7 +95,7 @@ public class StringListListEntry extends BaseListEntry<String, StringListListEnt
             this.isSelected = isSelected;
             this.isSelected = isSelected;
             widget.render(mouseX, mouseY, delta);
             widget.render(mouseX, mouseY, delta);
             if (isSelected && listListEntry.isEditable())
             if (isSelected && listListEntry.isEditable())
-                fill(x, y + 12, x + entryWidth - 12, y + 13, getError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
+                fill(x, y + 12, x + entryWidth - 12, y + 13, getConfigError().isPresent() ? 0xffff5555 : 0xffe0e0e0);
         }
         }
         
         
         @Override
         @Override

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

@@ -81,12 +81,12 @@ public abstract class TextFieldListEntry<T> extends TooltipListEntry<T> {
         this.textFieldWidget.setIsEditable(isEditable());
         this.textFieldWidget.setIsEditable(isEditable());
         this.textFieldWidget.y = y + 1;
         this.textFieldWidget.y = y + 1;
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getStringWidth(I18n.translate(getFieldName())), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.resetButton.x = x;
             this.textFieldWidget.x = x + resetButton.getWidth();
             this.textFieldWidget.x = x + resetButton.getWidth();
             setTextFieldWidth(textFieldWidget, 148 - resetButton.getWidth() - 4);
             setTextFieldWidth(textFieldWidget, 148 - resetButton.getWidth() - 4);
         } else {
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate(getFieldName()), x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.textFieldWidget.x = x + entryWidth - 148;
             this.textFieldWidget.x = x + entryWidth - 148;
             setTextFieldWidth(textFieldWidget, 148 - resetButton.getWidth() - 4);
             setTextFieldWidth(textFieldWidget, 148 - resetButton.getWidth() - 4);

+ 24 - 3
src/main/java/me/shedaniel/clothconfig2/impl/ConfigBuilderImpl.java

@@ -13,6 +13,7 @@ import net.minecraft.util.Pair;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 
 
 public class ConfigBuilderImpl implements ConfigBuilder {
 public class ConfigBuilderImpl implements ConfigBuilder {
@@ -29,10 +30,11 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     private Consumer<Screen> afterInitConsumer = screen -> {};
     private Consumer<Screen> afterInitConsumer = screen -> {};
     private Map<String, Identifier> categoryBackground = Maps.newHashMap();
     private Map<String, Identifier> categoryBackground = Maps.newHashMap();
     private Map<String, List<Pair<String, Object>>> dataMap = Maps.newLinkedHashMap();
     private Map<String, List<Pair<String, Object>>> dataMap = Maps.newLinkedHashMap();
+    private String fallbackCategory = null;
     
     
     @Deprecated
     @Deprecated
     public ConfigBuilderImpl() {
     public ConfigBuilderImpl() {
-
+    
     }
     }
     
     
     @Override
     @Override
@@ -41,6 +43,12 @@ public class ConfigBuilderImpl implements ConfigBuilder {
         return this;
         return this;
     }
     }
     
     
+    @Override
+    public ConfigBuilder setFallbackCategory(ConfigCategory fallbackCategory) {
+        this.fallbackCategory = Objects.requireNonNull(fallbackCategory).getCategoryKey();
+        return this;
+    }
+    
     @Override
     @Override
     public Screen getParentScreen() {
     public Screen getParentScreen() {
         return parent;
         return parent;
@@ -77,23 +85,29 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     @Override
     @Override
     public ConfigCategory getOrCreateCategory(String categoryKey) {
     public ConfigCategory getOrCreateCategory(String categoryKey) {
         if (dataMap.containsKey(categoryKey))
         if (dataMap.containsKey(categoryKey))
-            return new ConfigCategoryImpl(identifier -> {
+            return new ConfigCategoryImpl(categoryKey, identifier -> {
                 categoryBackground.put(categoryKey, identifier);
                 categoryBackground.put(categoryKey, identifier);
             }, () -> dataMap.get(categoryKey));
             }, () -> dataMap.get(categoryKey));
         dataMap.put(categoryKey, Lists.newArrayList());
         dataMap.put(categoryKey, Lists.newArrayList());
-        return new ConfigCategoryImpl(identifier -> {
+        if (fallbackCategory == null)
+            fallbackCategory = categoryKey;
+        return new ConfigCategoryImpl(categoryKey, identifier -> {
             categoryBackground.put(categoryKey, identifier);
             categoryBackground.put(categoryKey, identifier);
         }, () -> dataMap.get(categoryKey));
         }, () -> dataMap.get(categoryKey));
     }
     }
     
     
     @Override
     @Override
     public ConfigBuilder removeCategory(String category) {
     public ConfigBuilder removeCategory(String category) {
+        if (dataMap.containsKey(category) && fallbackCategory.equals(category))
+            fallbackCategory = null;
         dataMap.remove(category);
         dataMap.remove(category);
         return this;
         return this;
     }
     }
     
     
     @Override
     @Override
     public ConfigBuilder removeCategoryIfExists(String category) {
     public ConfigBuilder removeCategoryIfExists(String category) {
+        if (dataMap.containsKey(category) && fallbackCategory.equals(category))
+            fallbackCategory = null;
         if (dataMap.containsKey(category))
         if (dataMap.containsKey(category))
             dataMap.remove(category);
             dataMap.remove(category);
         return this;
         return this;
@@ -172,6 +186,8 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     
     
     @Override
     @Override
     public Screen build() {
     public Screen build() {
+        if (dataMap.isEmpty() || fallbackCategory == null)
+            throw new NullPointerException("There cannot be no categories or fallback category!");
         ClothConfigScreen screen = new ClothConfigScreen(parent, I18n.translate(title), dataMap, doesConfirmSave, doesProcessErrors, listSmoothScroll, defaultBackground, categoryBackground) {
         ClothConfigScreen screen = new ClothConfigScreen(parent, I18n.translate(title), dataMap, doesConfirmSave, doesProcessErrors, listSmoothScroll, defaultBackground, categoryBackground) {
             @Override
             @Override
             public void onSave(Map<String, List<Pair<String, Object>>> o) {
             public void onSave(Map<String, List<Pair<String, Object>>> o) {
@@ -189,6 +205,11 @@ public class ConfigBuilderImpl implements ConfigBuilder {
                 super.init();
                 super.init();
                 afterInitConsumer.accept(this);
                 afterInitConsumer.accept(this);
             }
             }
+            
+            @Override
+            public String getFallbackCategory() {
+                return fallbackCategory;
+            }
         };
         };
         screen.setSmoothScrollingTabs(tabsSmoothScroll);
         screen.setSmoothScrollingTabs(tabsSmoothScroll);
         return screen;
         return screen;

+ 8 - 1
src/main/java/me/shedaniel/clothconfig2/impl/ConfigCategoryImpl.java

@@ -14,10 +14,17 @@ public class ConfigCategoryImpl implements ConfigCategory {
     
     
     private Supplier<List<Pair<String, Object>>> listSupplier;
     private Supplier<List<Pair<String, Object>>> listSupplier;
     private Consumer<Identifier> backgroundConsumer;
     private Consumer<Identifier> backgroundConsumer;
+    private String categoryKey;
     
     
-    ConfigCategoryImpl(Consumer<Identifier> backgroundConsumer, Supplier<List<Pair<String, Object>>> listSupplier) {
+    ConfigCategoryImpl(String categoryKey, Consumer<Identifier> backgroundConsumer, Supplier<List<Pair<String, Object>>> listSupplier) {
         this.listSupplier = listSupplier;
         this.listSupplier = listSupplier;
         this.backgroundConsumer = backgroundConsumer;
         this.backgroundConsumer = backgroundConsumer;
+        this.categoryKey = categoryKey;
+    }
+    
+    @Override
+    public String getCategoryKey() {
+        return categoryKey;
     }
     }
     
     
     @Override
     @Override

+ 11 - 0
src/main/java/me/shedaniel/clothconfig2/impl/builders/DoubleListBuilder.java

@@ -12,6 +12,7 @@ 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;
     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;
@@ -26,6 +27,15 @@ public class DoubleListBuilder extends FieldBuilder<List<Double>, DoubleListList
         this.value = value;
         this.value = value;
     }
     }
     
     
+    public Function<Double, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public DoubleListBuilder setCellErrorSupplier(Function<Double, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+        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;
@@ -140,6 +150,7 @@ public class DoubleListBuilder extends FieldBuilder<List<Double>, DoubleListList
             entry.setMaximum(max);
             entry.setMaximum(max);
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
+        entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setAddTooltip(addTooltip);
         entry.setAddTooltip(addTooltip);
         entry.setRemoveTooltip(removeTooltip);
         entry.setRemoveTooltip(removeTooltip);

+ 11 - 0
src/main/java/me/shedaniel/clothconfig2/impl/builders/FloatListBuilder.java

@@ -12,6 +12,7 @@ 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;
     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;
@@ -26,6 +27,15 @@ public class FloatListBuilder extends FieldBuilder<List<Float>, FloatListListEnt
         this.value = value;
         this.value = value;
     }
     }
     
     
+    public Function<Float, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public FloatListBuilder setCellErrorSupplier(Function<Float, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+        return this;
+    }
+    
     public FloatListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
     public FloatListBuilder setDeleteButtonEnabled(boolean deleteButtonEnabled) {
         this.deleteButtonEnabled = deleteButtonEnabled;
         this.deleteButtonEnabled = deleteButtonEnabled;
         return this;
         return this;
@@ -140,6 +150,7 @@ public class FloatListBuilder extends FieldBuilder<List<Float>, FloatListListEnt
             entry.setMaximum(max);
             entry.setMaximum(max);
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
+        entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setAddTooltip(addTooltip);
         entry.setAddTooltip(addTooltip);
         entry.setRemoveTooltip(removeTooltip);
         entry.setRemoveTooltip(removeTooltip);

+ 11 - 0
src/main/java/me/shedaniel/clothconfig2/impl/builders/IntListBuilder.java

@@ -12,6 +12,7 @@ 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;
     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;
@@ -26,6 +27,15 @@ public class IntListBuilder extends FieldBuilder<List<Integer>, IntegerListListE
         this.value = value;
         this.value = value;
     }
     }
     
     
+    public Function<Integer, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public IntListBuilder setCellErrorSupplier(Function<Integer, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+        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;
@@ -140,6 +150,7 @@ public class IntListBuilder extends FieldBuilder<List<Integer>, IntegerListListE
             entry.setMaximum(max);
             entry.setMaximum(max);
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
+        entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setAddTooltip(addTooltip);
         entry.setAddTooltip(addTooltip);
         entry.setRemoveTooltip(removeTooltip);
         entry.setRemoveTooltip(removeTooltip);

+ 11 - 0
src/main/java/me/shedaniel/clothconfig2/impl/builders/LongListBuilder.java

@@ -12,6 +12,7 @@ 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;
     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;
@@ -26,6 +27,15 @@ public class LongListBuilder extends FieldBuilder<List<Long>, LongListListEntry>
         this.value = value;
         this.value = value;
     }
     }
     
     
+    public Function<Long, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public LongListBuilder setCellErrorSupplier(Function<Long, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+        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;
@@ -140,6 +150,7 @@ public class LongListBuilder extends FieldBuilder<List<Long>, LongListListEntry>
             entry.setMaximum(max);
             entry.setMaximum(max);
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
+        entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setAddTooltip(addTooltip);
         entry.setAddTooltip(addTooltip);
         entry.setRemoveTooltip(removeTooltip);
         entry.setRemoveTooltip(removeTooltip);

+ 11 - 0
src/main/java/me/shedaniel/clothconfig2/impl/builders/StringListBuilder.java

@@ -12,6 +12,7 @@ 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 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;
@@ -25,6 +26,15 @@ public class StringListBuilder extends FieldBuilder<List<String>, StringListList
         this.value = value;
         this.value = value;
     }
     }
     
     
+    public Function<String, Optional<String>> getCellErrorSupplier() {
+        return cellErrorSupplier;
+    }
+    
+    public StringListBuilder setCellErrorSupplier(Function<String, Optional<String>> cellErrorSupplier) {
+        this.cellErrorSupplier = cellErrorSupplier;
+        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;
@@ -105,6 +115,7 @@ public class StringListBuilder extends FieldBuilder<List<String>, StringListList
         StringListListEntry entry = new StringListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart());
         StringListListEntry entry = new StringListListEntry(getFieldNameKey(), value, expended, null, saveConsumer, defaultValue, getResetButtonKey(), isRequireRestart());
         if (createNewInstance != null)
         if (createNewInstance != null)
             entry.setCreateNewInstance(createNewInstance);
             entry.setCreateNewInstance(createNewInstance);
+        entry.setCellErrorSupplier(cellErrorSupplier);
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setTooltipSupplier(() -> tooltipSupplier.apply(entry.getValue()));
         entry.setAddTooltip(addTooltip);
         entry.setAddTooltip(addTooltip);
         entry.setRemoveTooltip(removeTooltip);
         entry.setRemoveTooltip(removeTooltip);