Browse Source

Improved Edit Detection.

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 5 years ago
parent
commit
1e96384c48
26 changed files with 490 additions and 194 deletions
  1. 1 1
      gradle.properties
  2. 64 7
      src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java
  3. 19 0
      src/main/java/me/shedaniel/clothconfig2/api/AbstractConfigEntry.java
  4. 14 2
      src/main/java/me/shedaniel/clothconfig2/api/ConfigBuilder.java
  5. 25 0
      src/main/java/me/shedaniel/clothconfig2/api/ConfigScreen.java
  6. 10 0
      src/main/java/me/shedaniel/clothconfig2/api/TabbedConfigScreen.java
  7. 74 0
      src/main/java/me/shedaniel/clothconfig2/gui/AbstractConfigScreen.java
  8. 29 0
      src/main/java/me/shedaniel/clothconfig2/gui/AbstractTabbedConfigScreen.java
  9. 74 99
      src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java
  10. 0 1
      src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractListListEntry.java
  11. 0 3
      src/main/java/me/shedaniel/clothconfig2/gui/entries/AbstractTextFieldListListEntry.java
  12. 7 1
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListCell.java
  13. 26 5
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListEntry.java
  14. 10 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/BooleanListEntry.java
  15. 15 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/ColorEntry.java
  16. 20 8
      src/main/java/me/shedaniel/clothconfig2/gui/entries/DropdownBoxEntry.java
  17. 22 7
      src/main/java/me/shedaniel/clothconfig2/gui/entries/IntegerSliderEntry.java
  18. 3 2
      src/main/java/me/shedaniel/clothconfig2/gui/entries/KeyCodeEntry.java
  19. 10 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/LongSliderEntry.java
  20. 17 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/MultiElementListEntry.java
  21. 12 11
      src/main/java/me/shedaniel/clothconfig2/gui/entries/NestedListListEntry.java
  22. 11 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/SelectionListEntry.java
  23. 12 4
      src/main/java/me/shedaniel/clothconfig2/gui/entries/SubCategoryListEntry.java
  24. 12 7
      src/main/java/me/shedaniel/clothconfig2/gui/entries/TextFieldListEntry.java
  25. 2 15
      src/main/java/me/shedaniel/clothconfig2/impl/ConfigBuilderImpl.java
  26. 1 1
      src/main/java/me/shedaniel/clothconfig2/impl/ConfigCategoryImpl.java

+ 1 - 1
gradle.properties

@@ -3,6 +3,6 @@ minecraft_version=20w18a
 yarn_mappings=20w18a+build.1
 loader_version=0.8.2+build.194
 fabric_version=0.7.1+build.331-1.16
-mod_version=4.2.0-unstable
+mod_version=4.3.0-unstable
 modmenu_version=1.11.2+build.6
 nec_version=1.2.3+1.15.1

+ 64 - 7
src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java

@@ -1,11 +1,9 @@
 package me.shedaniel.clothconfig2;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import me.shedaniel.clothconfig2.api.*;
-import me.shedaniel.clothconfig2.gui.entries.DoubleListEntry;
-import me.shedaniel.clothconfig2.gui.entries.DropdownBoxEntry;
-import me.shedaniel.clothconfig2.gui.entries.LongSliderEntry;
-import me.shedaniel.clothconfig2.gui.entries.TooltipListEntry;
+import me.shedaniel.clothconfig2.gui.entries.*;
 import me.shedaniel.clothconfig2.impl.EasingMethod;
 import me.shedaniel.clothconfig2.impl.EasingMethod.EasingMethodImpl;
 import me.shedaniel.clothconfig2.impl.EasingMethods;
@@ -191,7 +189,6 @@ public class ClothConfigInitializer implements ClientModInitializer {
         LongSliderEntry scrollDurationEntry = entryBuilder.startLongSlider(new TranslatableText("option.cloth-config.scrollDuration"), scrollDuration, 0, 5000).setTextGetter(integer -> new LiteralText(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(new TranslatableText("option.cloth-config.scrollStep"), scrollStep).setDefaultValue(19).setSaveConsumer(i -> scrollStep = i).build();
         LongSliderEntry bounceMultiplierEntry = entryBuilder.startLongSlider(new TranslatableText("option.cloth-config.bounceBackMultiplier"), (long) (bounceBackMultiplier * 1000), -10, 750).setTextGetter(integer -> new LiteralText(integer < 0 ? "Value: Disabled" : String.format("Value: %s", integer / 1000d))).setDefaultValue(240).setSaveConsumer(i -> bounceBackMultiplier = i / 1000d).build();
-        
         scrolling.addEntry(new TooltipListEntry<Object>(new TranslatableText("option.cloth-config.setDefaultSmoothScroll"), null) {
             final int width = 220;
             private final AbstractButtonWidget buttonWidget = new AbstractPressableButtonWidget(0, 0, 0, 20, getFieldName()) {
@@ -201,7 +198,6 @@ public class ClothConfigInitializer implements ClientModInitializer {
                     scrollDurationEntry.setValue(600);
                     scrollStepEntry.setValue("19.0");
                     bounceMultiplierEntry.setValue(240);
-                    getScreen().setEdited(true, isRequiresRestart());
                 }
             };
             private final List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
@@ -246,7 +242,6 @@ public class ClothConfigInitializer implements ClientModInitializer {
                     scrollDurationEntry.setValue(0);
                     scrollStepEntry.setValue("16.0");
                     bounceMultiplierEntry.setValue(-10);
-                    getScreen().setEdited(true, isRequiresRestart());
                 }
             };
             private final List<AbstractButtonWidget> children = ImmutableList.of(buttonWidget);
@@ -291,6 +286,42 @@ public class ClothConfigInitializer implements ClientModInitializer {
     }
     
     public static ConfigBuilder getConfigBuilderWithDemo() {
+        class Pair<T, R> {
+            T t;
+            R r;
+        
+            public Pair(T t, R r) {
+                this.t = t;
+                this.r = r;
+            }
+        
+            public T getLeft() {
+                return t;
+            }
+        
+            public R getRight() {
+                return r;
+            }
+        
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+            
+                Pair<?, ?> pair = (Pair<?, ?>) o;
+            
+                if (!Objects.equals(t, pair.t)) return false;
+                return Objects.equals(r, pair.r);
+            }
+        
+            @Override
+            public int hashCode() {
+                int result = t != null ? t.hashCode() : 0;
+                result = 31 * result + (r != null ? r.hashCode() : 0);
+                return result;
+            }
+        }
+        
         ConfigBuilder builder = getConfigBuilder();
         ConfigEntryBuilder entryBuilder = builder.entryBuilder();
         ConfigCategory testing = builder.getOrCreateCategory(new TranslatableText("category.cloth-config.testing"));
@@ -302,12 +333,38 @@ public class ClothConfigInitializer implements ClientModInitializer {
         SubCategoryBuilder colors = entryBuilder.startSubCategory(new LiteralText("Colors")).setExpanded(true);
         colors.add(entryBuilder.startColorField(new LiteralText("A color field"), 0x00ffff).setDefaultValue(0x00ffff).build());
         colors.add(entryBuilder.startColorField(new LiteralText("An alpha color field"), 0xff00ffff).setDefaultValue(0xff00ffff).setAlphaMode(true).build());
+        colors.add(entryBuilder.startColorField(new LiteralText("An alpha color field"), 0xffffffff).setDefaultValue(0xffff0000).setAlphaMode(true).build());
         colors.add(entryBuilder.startDropdownMenu(new LiteralText("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());
         colors.add(entryBuilder.startDropdownMenu(new LiteralText("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());
         colors.add(entryBuilder.startDropdownMenu(new LiteralText("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());
         colors.add(entryBuilder.startDropdownMenu(new LiteralText("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());
         colors.add(entryBuilder.startDropdownMenu(new LiteralText("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(colors.build());
+        testing.addEntry(new NestedListListEntry<Pair<Integer, Integer>, MultiElementListEntry<Pair<Integer, Integer>>>(
+                new LiteralText("Nice"),
+                Lists.newArrayList(new Pair<>(10, 10), new Pair<>(20, 40)),
+                false,
+                Optional::empty,
+                list -> {},
+                () -> Lists.newArrayList(new Pair<>(10, 10), new Pair<>(20, 40)),
+                entryBuilder.getResetButtonKey(),
+                true,
+                true,
+                (elem, nestedListListEntry) -> {
+                    if (elem == null) {
+                        Pair<Integer, Integer> newDefaultElemValue = new Pair<>(10, 10);
+                        return new MultiElementListEntry<>(new LiteralText("Pair"), newDefaultElemValue,
+                                Lists.newArrayList(entryBuilder.startIntField(new LiteralText("Left"), newDefaultElemValue.getLeft()).setDefaultValue(10).build(),
+                                        entryBuilder.startIntField(new LiteralText("Right"), newDefaultElemValue.getRight()).setDefaultValue(10).build()),
+                                true);
+                    } else {
+                        return new MultiElementListEntry<>(new LiteralText("Pair"), elem,
+                                Lists.newArrayList(entryBuilder.startIntField(new LiteralText("Left"), elem.getLeft()).setDefaultValue(10).build(),
+                                        entryBuilder.startIntField(new LiteralText("Right"), elem.getRight()).setDefaultValue(10).build()),
+                                true);
+                    }
+                }
+        ));
         return builder;
     }
     

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

@@ -5,7 +5,9 @@ import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.MutableText;
 import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
 
 import java.util.Optional;
 import java.util.function.Supplier;
@@ -21,6 +23,19 @@ public abstract class AbstractConfigEntry<T> extends DynamicElementListWidget.El
     
     public abstract Text getFieldName();
     
+    public Text getDisplayedFieldName() {
+        MutableText text = getFieldName().shallowCopy();
+        boolean hasError = getConfigError().isPresent();
+        boolean isEdited = isEdited();
+        if (hasError)
+            text = text.formatted(Formatting.RED);
+        if (isEdited)
+            text = text.formatted(Formatting.ITALIC);
+        if (!hasError && !isEdited)
+            text = text.formatted(Formatting.GRAY);
+        return text;
+    }
+    
     public abstract T getValue();
     
     public final Optional<Text> getConfigError() {
@@ -58,6 +73,10 @@ public abstract class AbstractConfigEntry<T> extends DynamicElementListWidget.El
     
     public abstract void save();
     
+    public boolean isEdited() {
+        return getConfigError().isPresent();
+    }
+    
     @Override
     public int getItemHeight() {
         return 24;

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

@@ -52,9 +52,21 @@ public interface ConfigBuilder {
     
     boolean doesConfirmSave();
     
-    ConfigBuilder setDoesProcessErrors(boolean processErrors);
+    /**
+     * This feature has been removed.
+     */
+    @Deprecated
+    default ConfigBuilder setDoesProcessErrors(boolean processErrors) {
+        return this;
+    }
     
-    boolean doesProcessErrors();
+    /**
+     * This feature has been removed.
+     */
+    @Deprecated
+    default boolean doesProcessErrors() {
+        return false;
+    }
     
     Identifier getDefaultBackgroundTexture();
     

+ 25 - 0
src/main/java/me/shedaniel/clothconfig2/api/ConfigScreen.java

@@ -0,0 +1,25 @@
+package me.shedaniel.clothconfig2.api;
+
+import net.minecraft.util.Identifier;
+
+public interface ConfigScreen {
+    Identifier getBackgroundLocation();
+    
+    boolean isRequiresRestart();
+    
+    boolean isEdited();
+    
+    /**
+     * Override #isEdited please
+     */
+    @Deprecated
+    void setEdited(boolean edited);
+    
+    /**
+     * Override #isEdited please
+     */
+    @Deprecated
+    void setEdited(boolean edited, boolean legacyRequiresRestart);
+    
+    void saveAll(boolean openOtherScreens);
+}

+ 10 - 0
src/main/java/me/shedaniel/clothconfig2/api/TabbedConfigScreen.java

@@ -0,0 +1,10 @@
+package me.shedaniel.clothconfig2.api;
+
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+
+public interface TabbedConfigScreen extends ConfigScreen {
+    void registerCategoryBackground(Text text, Identifier identifier);
+    
+    Text getSelectedCategory();
+}

+ 74 - 0
src/main/java/me/shedaniel/clothconfig2/gui/AbstractConfigScreen.java

@@ -0,0 +1,74 @@
+package me.shedaniel.clothconfig2.gui;
+
+import me.shedaniel.clothconfig2.api.AbstractConfigEntry;
+import me.shedaniel.clothconfig2.api.ConfigScreen;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class AbstractConfigScreen extends Screen implements ConfigScreen {
+    private boolean legacyEdited = false;
+    protected boolean legacyRequiresRestart = false;
+    private final Identifier backgroundLocation;
+    
+    protected AbstractConfigScreen(Text title, Identifier backgroundLocation) {
+        super(title);
+        this.backgroundLocation = backgroundLocation;
+    }
+    
+    @Override
+    public Identifier getBackgroundLocation() {
+        return backgroundLocation;
+    }
+    
+    @Override
+    public boolean isRequiresRestart() {
+        if (legacyRequiresRestart) return true;
+        for (List<AbstractConfigEntry<?>> entries : getCategorizedEntries().values()) {
+            for (AbstractConfigEntry<?> entry : entries) {
+                if (entry.isRequiresRestart()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    public abstract Map<Text, List<AbstractConfigEntry<?>>> getCategorizedEntries();
+    
+    @Override
+    public boolean isEdited() {
+        if (legacyEdited) return true;
+        for (List<AbstractConfigEntry<?>> entries : getCategorizedEntries().values()) {
+            for (AbstractConfigEntry<?> entry : entries) {
+                if (entry.isEdited()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Override #isEdited please
+     */
+    @Override
+    @Deprecated
+    public void setEdited(boolean edited) {
+        this.legacyEdited = edited;
+    }
+    
+    /**
+     * Override #isEdited please
+     */
+    @Override
+    @Deprecated
+    public void setEdited(boolean edited, boolean legacyRequiresRestart) {
+        setEdited(edited);
+        if (!this.legacyRequiresRestart && legacyRequiresRestart)
+            this.legacyRequiresRestart = legacyRequiresRestart;
+    }
+}

+ 29 - 0
src/main/java/me/shedaniel/clothconfig2/gui/AbstractTabbedConfigScreen.java

@@ -0,0 +1,29 @@
+package me.shedaniel.clothconfig2.gui;
+
+import com.google.common.collect.Maps;
+import me.shedaniel.clothconfig2.api.TabbedConfigScreen;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+
+import java.util.Map;
+
+public abstract class AbstractTabbedConfigScreen extends AbstractConfigScreen implements TabbedConfigScreen {
+    private final Map<Text, Identifier> categoryBackgroundLocation = Maps.newHashMap();
+    
+    protected AbstractTabbedConfigScreen(Text title, Identifier backgroundLocation) {
+        super(title, backgroundLocation);
+    }
+    
+    @Override
+    public final void registerCategoryBackground(Text text, Identifier identifier) {
+        this.categoryBackgroundLocation.put(text, identifier);
+    }
+    
+    @Override
+    public Identifier getBackgroundLocation() {
+        Text selectedCategory = getSelectedCategory();
+        if (categoryBackgroundLocation.containsKey(selectedCategory))
+            return categoryBackgroundLocation.get(selectedCategory);
+        return super.getBackgroundLocation();
+    }
+}

+ 74 - 99
src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java

@@ -38,36 +38,27 @@ import org.jetbrains.annotations.Nullable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
-@SuppressWarnings({"deprecation", "rawtypes", "unchecked", "DuplicatedCode"})
+@SuppressWarnings({"deprecation", "rawtypes", "DuplicatedCode"})
 @Environment(EnvType.CLIENT)
-public abstract class ClothConfigScreen extends Screen {
-    
+public abstract class ClothConfigScreen extends AbstractTabbedConfigScreen {
     private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
     private final List<QueuedTooltip> queuedTooltips = Lists.newArrayList();
-    public int nextTabIndex;
-    public int selectedTabIndex;
+    public int nextTabIndex = 0;
+    public int selectedTabIndex = 0;
     public double tabsScrollVelocity = 0d;
-    public double tabsScrollProgress;
+    public double tabsScrollProgress = 0d;
     public ListWidget<AbstractConfigEntry<AbstractConfigEntry>> listWidget;
     private KeyCodeEntry focusedBinding;
     private final Screen parent;
-    private final LinkedHashMap<Text, List<AbstractConfigEntry>> tabbedEntries;
+    private final LinkedHashMap<Text, List<AbstractConfigEntry<?>>> categorizedEntries = Maps.newLinkedHashMap();
     private final List<Pair<Text, Integer>> tabs;
-    private boolean edited;
-    private boolean requiresRestart;
     private final boolean confirmSave;
     private AbstractButtonWidget quitButton, saveButton, applyButton, buttonLeftTab, buttonRightTab;
     private Rectangle tabsBounds, tabsLeftBounds, tabsRightBounds;
     private double tabsMaximumScrolled = -1d;
-    private final boolean displayErrors;
-    private final List<ClothConfigTabButton> tabButtons;
-    private boolean smoothScrollingTabs = true;
-    private boolean smoothScrollingList;
-    private final Identifier defaultBackgroundLocation;
-    private final Map<Text, Identifier> categoryBackgroundLocation;
+    private final List<ClothConfigTabButton> tabButtons = Lists.newArrayList();
     private boolean transparentBackground = false;
     private boolean editable = true;
     @Nullable private Text defaultFallbackCategory = null;
@@ -75,28 +66,20 @@ public abstract class ClothConfigScreen extends Screen {
     private ModifierKeyCode startedKeyCode = null;
     
     @Deprecated
-    public ClothConfigScreen(Screen parent, Text title, Map<Text, List<Pair<Text, Object>>> o, boolean confirmSave, boolean displayErrors, boolean smoothScrollingList, Identifier defaultBackgroundLocation, Map<Text, Identifier> categoryBackgroundLocation) {
-        super(title);
+    public ClothConfigScreen(Screen parent, Text title, Map<Text, List<Pair<Text, Object>>> entriesMap, boolean confirmSave, Identifier backgroundLocation) {
+        super(title, backgroundLocation);
         this.parent = parent;
-        this.tabbedEntries = Maps.newLinkedHashMap();
-        this.smoothScrollingList = smoothScrollingList;
-        this.defaultBackgroundLocation = defaultBackgroundLocation;
-        o.forEach((tab, pairs) -> {
-            List<AbstractConfigEntry> list = Lists.newArrayList();
+        entriesMap.forEach((categoryName, pairs) -> {
+            List<AbstractConfigEntry<?>> list = Lists.newArrayList();
             for (Pair<Text, Object> pair : pairs) {
-                if (pair.getRight() instanceof AbstractConfigListEntry) {
-                    list.add((AbstractConfigListEntry) pair.getRight());
-                } else {
-                    throw new IllegalArgumentException("Unsupported Type (" + pair.getLeft() + "): " + pair.getRight().getClass().getSimpleName());
-                }
+                AbstractConfigListEntry<?> entry = (AbstractConfigListEntry<?>) pair.getRight();
+                entry.setScreen(this);
+                list.add(entry);
             }
-            list.forEach(entry -> entry.setScreen(this));
-            tabbedEntries.put(tab, list);
+            categorizedEntries.put(categoryName, list);
         });
         TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
-        this.tabs = tabbedEntries.keySet().stream().map(s -> new Pair<>(s, textRenderer.getWidth(s) + 8)).collect(Collectors.toList());
-        this.nextTabIndex = 0;
-        this.selectedTabIndex = 0;
+        this.tabs = categorizedEntries.keySet().stream().map(s -> new Pair<>(s, textRenderer.getWidth(s) + 8)).collect(Collectors.toList());
         for (int i = 0; i < tabs.size(); i++) {
             Pair<Text, Integer> pair = tabs.get(i);
             if (pair.getLeft().equals(getFallbackCategory())) {
@@ -106,12 +89,16 @@ public abstract class ClothConfigScreen extends Screen {
             }
         }
         this.confirmSave = confirmSave;
-        this.edited = false;
-        this.requiresRestart = false;
-        this.tabsScrollProgress = 0d;
-        this.tabButtons = Lists.newArrayList();
-        this.displayErrors = displayErrors;
-        this.categoryBackgroundLocation = categoryBackgroundLocation;
+    }
+    
+    @Override
+    public Text getSelectedCategory() {
+        return tabs.get(selectedTabIndex).getLeft();
+    }
+    
+    @Override
+    public Map<Text, List<AbstractConfigEntry<?>>> getCategorizedEntries() {
+        return categorizedEntries;
     }
     
     public boolean isShowingTabs() {
@@ -161,79 +148,67 @@ public abstract class ClothConfigScreen extends Screen {
         for (Element child : children())
             if (child instanceof Tickable)
                 ((Tickable) child).tick();
+        boolean edited = isEdited();
+        quitButton.setMessage(edited ? new TranslatableText("text.cloth-config.cancel_discard") : new TranslatableText("gui.cancel"));
+        saveButton.active = edited;
     }
     
-    public Identifier getBackgroundLocation() {
-        if (categoryBackgroundLocation.containsKey(Lists.newArrayList(tabbedEntries.keySet()).get(selectedTabIndex)))
-            return categoryBackgroundLocation.get(Lists.newArrayList(tabbedEntries.keySet()).get(selectedTabIndex));
-        return defaultBackgroundLocation;
-    }
-    
-    public boolean isSmoothScrollingList() {
-        return smoothScrollingList;
-    }
-    
-    @Deprecated
-    public void setSmoothScrollingList(boolean smoothScrollingList) {
-        this.smoothScrollingList = smoothScrollingList;
-    }
-    
-    public boolean isSmoothScrollingTabs() {
-        return smoothScrollingTabs;
-    }
-    
-    @Deprecated
-    public void setSmoothScrollingTabs(boolean smoothScrolling) {
-        this.smoothScrollingTabs = smoothScrolling;
-    }
-    
+    @Override
     public boolean isEdited() {
-        return edited;
+        return super.isEdited();
     }
     
+    /**
+     * Override #isEdited please
+     */
     @Deprecated
     public void setEdited(boolean edited) {
-        this.edited = edited;
-        quitButton.setMessage(edited ? new TranslatableText("text.cloth-config.cancel_discard") : new TranslatableText("gui.cancel"));
-        saveButton.active = edited;
+        super.setEdited(edited);
     }
     
+    /**
+     * Override #isEdited please
+     */
+    @Override
+    @Deprecated
     public void setEdited(boolean edited, boolean requiresRestart) {
-        setEdited(edited);
-        if (!this.requiresRestart && requiresRestart)
-            this.requiresRestart = requiresRestart;
+        super.setEdited(edited, requiresRestart);
     }
     
+    @Override
     public void saveAll(boolean openOtherScreens) {
-        for (List<AbstractConfigEntry> entries : Lists.newArrayList(tabbedEntries.values()))
-            for (AbstractConfigEntry entry : entries)
+        for (List<AbstractConfigEntry<?>> entries : Lists.newArrayList(categorizedEntries.values()))
+            for (AbstractConfigEntry<?> entry : entries)
                 entry.save();
         save();
         setEdited(false);
         if (openOtherScreens) {
-            if (requiresRestart)
+            if (isRequiresRestart())
                 ClothConfigScreen.this.client.openScreen(new ClothRequiresRestartScreen(parent));
             else
                 ClothConfigScreen.this.client.openScreen(parent);
         }
-        requiresRestart = false;
+        this.legacyRequiresRestart = false;
     }
     
     @Override
     protected void init() {
         super.init();
+        // Clear children
         this.children.clear();
         this.tabButtons.clear();
+        
+        // Put already created list back to entries
         if (listWidget != null)
-            tabbedEntries.put(tabs.get(selectedTabIndex).getLeft(), (List) listWidget.children());
+            categorizedEntries.put(tabs.get(selectedTabIndex).getLeft(), (List) listWidget.children());
         selectedTabIndex = nextTabIndex;
         children.add(listWidget = new ListWidget(client, width, height, isShowingTabs() ? 70 : 30, height - 32, getBackgroundLocation()));
-        listWidget.setSmoothScrolling(this.smoothScrollingList);
-        if (tabbedEntries.size() > selectedTabIndex)
-            Lists.newArrayList(tabbedEntries.values()).get(selectedTabIndex).forEach(entry -> listWidget.children().add(entry));
+        if (categorizedEntries.size() > selectedTabIndex) {
+            listWidget.children().addAll((List) Lists.newArrayList(categorizedEntries.values()).get(selectedTabIndex));
+        }
         int buttonWidths = Math.min(200, (width - 50 - 12) / 3);
-        addButton(quitButton = new ButtonWidget(width / 2 - buttonWidths / 2 - buttonWidths - 6, height - 26, buttonWidths, 20, edited ? new TranslatableText("text.cloth-config.cancel_discard") : new TranslatableText("gui.cancel"), widget -> {
-            if (confirmSave && edited)
+        addButton(quitButton = new ButtonWidget(width / 2 - buttonWidths / 2 - buttonWidths - 6, height - 26, buttonWidths, 20, isEdited() ? new TranslatableText("text.cloth-config.cancel_discard") : new TranslatableText("gui.cancel"), widget -> {
+            if (confirmSave && isEdited())
                 client.openScreen(new ConfirmScreen(new QuitSaveConsumer(), new TranslatableText("text.cloth-config.quit_config"), new TranslatableText("text.cloth-config.quit_config_sure"), new TranslatableText("text.cloth-config.quit_discard"), new TranslatableText("gui.cancel")));
             else
                 client.openScreen(parent);
@@ -247,25 +222,24 @@ public abstract class ClothConfigScreen extends Screen {
             @Override
             public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
                 boolean hasErrors = false;
-                if (displayErrors)
-                    for (List<AbstractConfigEntry> entries : Lists.newArrayList(tabbedEntries.values())) {
-                        for (AbstractConfigEntry entry : entries)
-                            if (entry.getConfigError().isPresent()) {
-                                hasErrors = true;
-                                break;
-                            }
-                        if (hasErrors)
+                for (List<AbstractConfigEntry<?>> entries : Lists.newArrayList(categorizedEntries.values())) {
+                    for (AbstractConfigEntry<?> entry : entries)
+                        if (entry.getConfigError().isPresent()) {
+                            hasErrors = true;
                             break;
-                    }
-                active = edited && !hasErrors;
-                setMessage(displayErrors && hasErrors ? new TranslatableText("text.cloth-config.error_cannot_save") : new TranslatableText("text.cloth-config.save_and_done"));
+                        }
+                    if (hasErrors)
+                        break;
+                }
+                active = isEdited() && !hasErrors;
+                setMessage(hasErrors ? new TranslatableText("text.cloth-config.error_cannot_save") : new TranslatableText("text.cloth-config.save_and_done"));
                 super.render(matrices, mouseX, mouseY, delta);
             }
         });
         addButton(applyButton = new AbstractPressableButtonWidget(width / 2 - buttonWidths / 2, height - 26, buttonWidths, 20, new TranslatableText("text.cloth-config.apply")) {
             @Override
             public void onPress() {
-                if (requiresRestart)
+                if (isRequiresRestart())
                     ClothConfigScreen.this.client.openScreen(new ClothRequiresRestartScreen(ClothConfigScreen.this));
                 saveAll(false);
             }
@@ -276,7 +250,7 @@ public abstract class ClothConfigScreen extends Screen {
                 super.render(matrices, mouseX, mouseY, delta);
             }
         });
-        saveButton.active = edited;
+        saveButton.active = isEdited();
         if (isShowingTabs()) {
             tabsBounds = new Rectangle(0, 41, width, 24);
             tabsLeftBounds = new Rectangle(0, 41, 18, 24);
@@ -369,7 +343,8 @@ public abstract class ClothConfigScreen extends Screen {
     @Override
     public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
         if (isShowingTabs()) {
-            if (smoothScrollingTabs) {
+            // TODO Rewrite code for smooth tabs
+            if (true) {
                 double change = tabsScrollVelocity * 0.2f;
                 if (change != 0) {
                     if (change > 0 && change < .2)
@@ -421,12 +396,12 @@ public abstract class ClothConfigScreen extends Screen {
         } else
             method_27534(matrices, client.textRenderer, title, width / 2, 12, -1);
         
-        if (displayErrors && isEditable()) {
+        if (isEditable()) {
             List<Text> errors = Lists.newArrayList();
-            for (List<AbstractConfigEntry> entries : Lists.newArrayList(tabbedEntries.values()))
-                for (AbstractConfigEntry entry : entries)
+            for (List<AbstractConfigEntry<?>> entries : Lists.newArrayList(categorizedEntries.values()))
+                for (AbstractConfigEntry<?> entry : entries)
                     if (entry.getConfigError().isPresent())
-                        errors.add(((Optional<Text>) entry.getConfigError()).get());
+                        errors.add(entry.getConfigError().get());
             if (errors.size() > 0) {
                 client.getTextureManager().bindTexture(CONFIG_TEX);
                 RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
@@ -624,7 +599,7 @@ public abstract class ClothConfigScreen extends Screen {
         if (this.focusedBinding != null && int_1 != 256)
             return true;
         if (int_1 == 256 && this.shouldCloseOnEsc()) {
-            if (confirmSave && edited)
+            if (confirmSave && isEdited())
                 client.openScreen(new ConfirmScreen(new QuitSaveConsumer(), new TranslatableText("text.cloth-config.quit_config"), new TranslatableText("text.cloth-config.quit_config_sure"), new TranslatableText("text.cloth-config.quit_discard"), new TranslatableText("gui.cancel")));
             else
                 client.openScreen(parent);

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

@@ -70,7 +70,6 @@ public abstract class AbstractListListEntry<T, C extends AbstractListListEntry.A
         }
         
         public abstract T getValue();
-        
     }
     
 }

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

@@ -66,9 +66,6 @@ public abstract class AbstractTextFieldListListEntry<T, C extends AbstractTextFi
             widget.setText(Objects.toString(finalValue));
             widget.setChangedListener(s -> {
                 widget.setEditableColor(getPreferredTextColor());
-                if (listListEntry.getScreen() != null && !Objects.equals(s, Objects.toString(finalValue))) {
-                    this.listListEntry.getScreen().setEdited(true, this.listListEntry.isRequiresRestart());
-                }
             });
         }
         

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

@@ -11,7 +11,6 @@ import java.util.function.Supplier;
 
 @Environment(EnvType.CLIENT)
 public abstract class BaseListCell extends AbstractParentElement {
-    
     private Supplier<Optional<Text>> errorSupplier;
     
     public final int getPreferredTextColor() {
@@ -36,4 +35,11 @@ public abstract class BaseListCell extends AbstractParentElement {
     
     public void updateSelected(boolean isSelected) {}
     
+    public boolean isRequiresRestart() {
+        return false;
+    }
+    
+    public boolean isEdited() {
+        return getConfigError().isPresent();
+    }
 }

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

@@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -79,7 +80,6 @@ public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends Base
             cells.clear();
             defaultValue.get().stream().map(this::getFromValue).forEach(cells::add);
             widgets.addAll(cells);
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.widgets.add(resetWidget);
         this.saveConsumer = saveConsumer;
@@ -87,6 +87,29 @@ public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends Base
         this.defaultValue = defaultValue;
     }
     
+    @Override
+    public boolean isEdited() {
+        if (super.isEdited()) return true;
+        if (cells.stream().anyMatch(BaseListCell::isEdited)) return true;
+        List<T> value = getValue();
+        List<T> defaultValue = this.defaultValue.get();
+        if (value.size() != defaultValue.size()) return true;
+        for (int i = 0; i < value.size(); i++) {
+            if (!Objects.equals(value.get(i), defaultValue.get(i)))
+                return true;
+        }
+        return false;
+    }
+    
+    @Override
+    public boolean isRequiresRestart() {
+        return cells.stream().anyMatch(BaseListCell::isRequiresRestart);
+    }
+    
+    @Override
+    public void setRequiresRestart(boolean requiresRestart) {
+    }
+    
     public abstract SELF self();
     
     public boolean isDeleteButtonEnabled() {
@@ -214,9 +237,9 @@ public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends Base
             drawTexture(matrices, x - 15 + 26, y + 4, 24 + 27, focused == null ? 0 : insideDelete ? 18 : 9, 9, 9);
         resetWidget.x = x + entryWidth - resetWidget.getWidth();
         resetWidget.y = y;
-        resetWidget.active = isEditable() && getDefaultValue().isPresent();
+        resetWidget.active = isEdited();
         resetWidget.render(matrices, mouseX, mouseY, delta);
-        MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, 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(matrices, getDisplayedFieldName(), isDeleteButtonEnabled() ? x + 24 : x + 24 - 9, y + 5, labelWidget.rectangle.contains(mouseX, mouseY) && !resetWidget.isMouseOver(mouseX, mouseY) && !insideDelete && !insideCreateNew ? 0xffe6fe16 : getPreferredTextColor());
         if (expanded) {
             int yy = y + 24;
             for (BaseListCell cell : cells) {
@@ -254,7 +277,6 @@ public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends Base
                     cells.add(cell = createNewInstance.apply(BaseListEntry.this.self()));
                     widgets.add(cell);
                 }
-                getScreen().setEdited(true, isRequiresRestart());
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 return true;
             } else if (isDeleteButtonEnabled() && isInsideDelete(double_1, double_2)) {
@@ -263,7 +285,6 @@ public abstract class BaseListEntry<T, C extends BaseListCell, SELF extends Base
                     //noinspection SuspiciousMethodCalls
                     cells.remove(focused);
                     widgets.remove(focused);
-                    getScreen().setEdited(true, isRequiresRestart());
                     MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                 }
                 return true;

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

@@ -23,6 +23,7 @@ import java.util.function.Supplier;
 public class BooleanListEntry extends TooltipListEntry<Boolean> {
     
     private final AtomicBoolean bool;
+    private final boolean original;
     private final ButtonWidget buttonWidget;
     private final ButtonWidget resetButton;
     private final Consumer<Boolean> saveConsumer;
@@ -46,19 +47,23 @@ public class BooleanListEntry extends TooltipListEntry<Boolean> {
     public BooleanListEntry(Text fieldName, boolean bool, Text resetButtonKey, Supplier<Boolean> defaultValue, Consumer<Boolean> saveConsumer, Supplier<Optional<Text[]>> tooltipSupplier, boolean requiresRestart) {
         super(fieldName, tooltipSupplier, requiresRestart);
         this.defaultValue = defaultValue;
+        this.original = bool;
         this.bool = new AtomicBoolean(bool);
         this.buttonWidget = new ButtonWidget(0, 0, 150, 20, NarratorManager.EMPTY, widget -> {
             BooleanListEntry.this.bool.set(!BooleanListEntry.this.bool.get());
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
             BooleanListEntry.this.bool.set(defaultValue.get());
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.saveConsumer = saveConsumer;
         this.widgets = Lists.newArrayList(buttonWidget, resetButton);
     }
     
+    @Override
+    public boolean isEdited() {
+        return super.isEdited() || original != bool.get();
+    }
+    
     @Override
     public void save() {
         if (saveConsumer != null)
@@ -84,12 +89,13 @@ public class BooleanListEntry extends TooltipListEntry<Boolean> {
         this.buttonWidget.active = isEditable();
         this.buttonWidget.y = y;
         this.buttonWidget.setMessage(getYesNoText(bool.get()));
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, 16777215);
             this.resetButton.x = x;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.buttonWidget.x = x + entryWidth - 150;
         }

+ 15 - 4
src/main/java/me/shedaniel/clothconfig2/gui/entries/ColorEntry.java

@@ -32,15 +32,20 @@ public class ColorEntry extends TextFieldListEntry<Integer> {
             throw new IllegalArgumentException("Invalid Color: " + colorValue.getError().name());
         this.alpha = false;
         this.saveConsumer = saveConsumer;
+        this.original = value;
         this.textFieldWidget.setText(getHexColorString(value));
         this.colorDisplayWidget = new ColorDisplayWidget(textFieldWidget, 0, 0, 20, getColorValueColor(textFieldWidget.getText()));
-        this.original = value;
         ((ButtonWidgetHooks) this.resetButton).setOnPress(button -> {
-            this.textFieldWidget.setText(getHexColorString(original));
-            getScreen().setEdited(true, isRequiresRestart());
+            this.textFieldWidget.setText(getHexColorString(defaultValue.get()));
         });
     }
     
+    @Override
+    protected boolean isChanged(Integer original, String s) {
+        ColorValue colorValue = getColorValue(s);
+        return colorValue.hasError() || this.original != colorValue.color;
+    }
+    
     @Override
     public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) {
         super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta);
@@ -76,7 +81,13 @@ public class ColorEntry extends TextFieldListEntry<Integer> {
         if (!getDefaultValue().isPresent())
             return false;
         ColorValue colorValue = getColorValue(text);
-        return colorValue.hasError() && colorValue.color == getDefaultValue().get();
+        return !colorValue.hasError() && colorValue.color == getDefaultValue().get();
+    }
+    
+    @Override
+    public boolean isEdited() {
+        ColorValue colorValue = getColorValue(textFieldWidget.getText());
+        return colorValue.hasError() || colorValue.color != original;
     }
     
     @Override

+ 20 - 8
src/main/java/me/shedaniel/clothconfig2/gui/entries/DropdownBoxEntry.java

@@ -56,7 +56,6 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         this.saveConsumer = saveConsumer;
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
             selectionElement.topRenderer.setValue(defaultValue.get());
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.selectionElement = new SelectionElement<>(this, new Rectangle(0, 0, 150, 20), new DefaultDropdownMenuElement<>(selections == null ? ImmutableList.of() : ImmutableList.copyOf(selections)), topRenderer, cellCreator);
     }
@@ -69,12 +68,13 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         this.resetButton.y = y;
         this.selectionElement.active = isEditable();
         this.selectionElement.bounds.y = y;
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.selectionElement.bounds.x = x + resetButton.getWidth() + 1;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.selectionElement.bounds.x = x + entryWidth - 150 + 1;
         }
@@ -83,6 +83,11 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         selectionElement.render(matrices, mouseX, mouseY, delta);
     }
     
+    @Override
+    public boolean isEdited() {
+        return this.selectionElement.topRenderer.isEdited();
+    }
+    
     public boolean isSuggestionMode() {
         return suggestionMode;
     }
@@ -646,6 +651,10 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         
         public abstract Text getSearchTerm();
         
+        public boolean isEdited() {
+            return getConfigError().isPresent();
+        }
+        
         public abstract Optional<Text> getError();
         
         public final Optional<Text> getConfigError() {
@@ -659,7 +668,7 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         public final boolean hasConfigError() {
             return getConfigError().isPresent();
         }
-    
+        
         public final boolean hasError() {
             return getError().isPresent();
         }
@@ -690,9 +699,11 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
         protected TextFieldWidget textFieldWidget;
         protected Function<String, R> toObjectFunction;
         protected Function<R, Text> toTextFunction;
+        protected final R original;
         protected R value;
         
         public DefaultSelectionTopCellElement(R value, Function<String, R> toObjectFunction, Function<R, Text> toTextFunction) {
+            this.original = Objects.requireNonNull(value);
             this.value = Objects.requireNonNull(value);
             this.toObjectFunction = Objects.requireNonNull(toObjectFunction);
             this.toTextFunction = Objects.requireNonNull(toTextFunction);
@@ -720,10 +731,11 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             textFieldWidget.setHasBorder(false);
             textFieldWidget.setMaxLength(999999);
             textFieldWidget.setText(toTextFunction.apply(value).getString());
-            textFieldWidget.setChangedListener(s -> {
-                if (getParent() != null && getParent().getScreen() != null && !toTextFunction.apply(value).getString().equals(s))
-                    getParent().getScreen().setEdited(true, getParent().isRequiresRestart());
-            });
+        }
+        
+        @Override
+        public boolean isEdited() {
+            return super.isEdited() || !getValue().equals(original);
         }
         
         @Override

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

@@ -28,6 +28,7 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
     protected Slider sliderWidget;
     protected ButtonWidget resetButton;
     protected AtomicInteger value;
+    protected final long orginial;
     private int minimum, maximum;
     private Consumer<Integer> saveConsumer;
     private Supplier<Integer> defaultValue;
@@ -50,6 +51,7 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
     @Deprecated
     public IntegerSliderEntry(Text fieldName, int minimum, int maximum, int value, Text resetButtonKey, Supplier<Integer> defaultValue, Consumer<Integer> saveConsumer, Supplier<Optional<Text[]>> tooltipSupplier, boolean requiresRestart) {
         super(fieldName, tooltipSupplier, requiresRestart);
+        this.orginial = value;
         this.defaultValue = defaultValue;
         this.value = new AtomicInteger(value);
         this.saveConsumer = saveConsumer;
@@ -57,10 +59,7 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         this.minimum = minimum;
         this.sliderWidget = new Slider(0, 0, 152, 20, ((double) this.value.get() - minimum) / Math.abs(maximum - minimum));
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
-            sliderWidget.setProgress((MathHelper.clamp(this.defaultValue.get(), minimum, maximum) - minimum) / (double) Math.abs(maximum - minimum));
-            this.value.set(MathHelper.clamp(this.defaultValue.get(), minimum, maximum));
-            sliderWidget.updateMessage();
-            getScreen().setEdited(true, isRequiresRestart());
+            setValue(defaultValue.get());
         });
         this.sliderWidget.setMessage(textGetter.apply(IntegerSliderEntry.this.value.get()));
         this.widgets = Lists.newArrayList(sliderWidget, resetButton);
@@ -87,6 +86,18 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         return value.get();
     }
     
+    @Deprecated
+    public void setValue(int value) {
+        sliderWidget.setValue((MathHelper.clamp(value, minimum, maximum) - minimum) / (double) Math.abs(maximum - minimum));
+        this.value.set(Math.min(Math.max(value, minimum), maximum));
+        sliderWidget.updateMessage();
+    }
+    
+    @Override
+    public boolean isEdited() {
+        return super.isEdited() || getValue() != orginial;
+    }
+    
     @Override
     public Optional<Integer> getDefaultValue() {
         return defaultValue == null ? Optional.empty() : Optional.ofNullable(defaultValue.get());
@@ -115,12 +126,13 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         this.resetButton.y = y;
         this.sliderWidget.active = isEditable();
         this.sliderWidget.y = y;
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.sliderWidget.x = x + entryWidth - 150;
         }
@@ -142,7 +154,6 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         @Override
         protected void applyValue() {
             IntegerSliderEntry.this.value.set((int) (minimum + Math.abs(maximum - minimum) * value));
-            getScreen().setEdited(true, isRequiresRestart());
         }
         
         @Override
@@ -166,6 +177,10 @@ public class IntegerSliderEntry extends TooltipListEntry<Integer> {
         public void setProgress(double integer) {
             this.value = integer;
         }
+        
+        public void setValue(double integer) {
+            this.value = integer;
+        }
     }
     
 }

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

@@ -109,12 +109,13 @@ public class KeyCodeEntry extends TooltipListEntry<ModifierKeyCode> {
         this.buttonWidget.setMessage(getLocalizedName());
         if (getScreen().getFocusedBinding() == this)
             this.buttonWidget.setMessage(new LiteralText(  "> ").formatted(Formatting.WHITE).append(this.buttonWidget.getMessage().copy().formatted(Formatting.YELLOW)).append(new LiteralText(  " <").formatted(Formatting.WHITE)));
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, 16777215);
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, 16777215);
             this.resetButton.x = x;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.buttonWidget.x = x + entryWidth - 150;
         }

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

@@ -28,6 +28,7 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
     protected Slider sliderWidget;
     protected ButtonWidget resetButton;
     protected AtomicLong value;
+    protected final long orginial;
     private long minimum, maximum;
     private Consumer<Long> saveConsumer;
     private Supplier<Long> defaultValue;
@@ -50,6 +51,7 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
     @Deprecated
     public LongSliderEntry(Text fieldName, long minimum, long maximum, long value, Consumer<Long> saveConsumer, Text resetButtonKey, Supplier<Long> defaultValue, Supplier<Optional<Text[]>> tooltipSupplier, boolean requiresRestart) {
         super(fieldName, tooltipSupplier, requiresRestart);
+        this.orginial = value;
         this.defaultValue = defaultValue;
         this.value = new AtomicLong(value);
         this.saveConsumer = saveConsumer;
@@ -58,7 +60,6 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
         this.sliderWidget = new Slider(0, 0, 152, 20, ((double) LongSliderEntry.this.value.get() - minimum) / Math.abs(maximum - minimum));
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
             setValue(defaultValue.get());
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.sliderWidget.setMessage(textGetter.apply(LongSliderEntry.this.value.get()));
         this.widgets = Lists.newArrayList(sliderWidget, resetButton);
@@ -102,6 +103,11 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
         return widgets;
     }
     
+    @Override
+    public boolean isEdited() {
+        return super.isEdited() || getValue() != orginial;
+    }
+    
     public LongSliderEntry setMaximum(long maximum) {
         this.maximum = maximum;
         return this;
@@ -120,12 +126,13 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
         this.resetButton.y = y;
         this.sliderWidget.active = isEditable();
         this.sliderWidget.y = y;
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.sliderWidget.x = x + resetButton.getWidth() + 1;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.sliderWidget.x = x + entryWidth - 150;
         }
@@ -147,7 +154,6 @@ public class LongSliderEntry extends TooltipListEntry<Long> {
         @Override
         protected void applyValue() {
             LongSliderEntry.this.value.set((long) (minimum + Math.abs(maximum - minimum) * value));
-            getScreen().setEdited(true, isRequiresRestart());
         }
         
         @Override

+ 17 - 4
src/main/java/me/shedaniel/clothconfig2/gui/entries/MultiElementListEntry.java

@@ -28,7 +28,6 @@ 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 Text categoryName;
     private List<AbstractConfigListEntry<?>> entries;
     private MultiElementListEntry<T>.CategoryLabelWidget widget;
     private List<Element> children;
@@ -38,7 +37,6 @@ public class MultiElementListEntry<T> extends TooltipListEntry<T> {
     @Deprecated
     public MultiElementListEntry(Text categoryName, T object, List<AbstractConfigListEntry<?>> entries, boolean defaultExpanded) {
         super(categoryName, null);
-        this.categoryName = categoryName;
         this.object = object;
         this.entries = entries;
         this.expanded = defaultExpanded;
@@ -55,13 +53,28 @@ public class MultiElementListEntry<T> extends TooltipListEntry<T> {
         return false;
     }
     
+    @Override
+    public boolean isEdited() {
+        for (AbstractConfigListEntry<?> entry : entries) {
+            if (entry.isEdited()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
     @Override
     public void setRequiresRestart(boolean requiresRestart) {
         
     }
     
+    @Override
+    public boolean mouseClicked(double mouseX, double mouseY, int button) {
+        return super.mouseClicked(mouseX, mouseY, button);
+    }
+    
     public Text getCategoryName() {
-        return categoryName;
+        return getFieldName();
     }
     
     @Override
@@ -85,7 +98,7 @@ public class MultiElementListEntry<T> extends TooltipListEntry<T> {
         DiffuseLighting.disable();
         RenderSystem.color4f(1, 1, 1, 1);
         drawTexture(matrices, x - 15, y + 4, 24, (widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (expanded ? 9 : 0), 9, 9);
-        MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, categoryName, x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
+        MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getDisplayedFieldName(), x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
         for (AbstractConfigListEntry<?> entry : entries) {
             entry.setParent(getParent());
             entry.setScreen(getScreen());

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

@@ -30,15 +30,6 @@ public final class NestedListListEntry<T, INNER extends AbstractConfigListEntry<
         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;
@@ -49,7 +40,6 @@ public final class NestedListListEntry<T, INNER extends AbstractConfigListEntry<
      * @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
@@ -84,9 +74,20 @@ public final class NestedListListEntry<T, INNER extends AbstractConfigListEntry<
             return Collections.singletonList(nestedEntry);
         }
         
-        private boolean isRequiresRestart() {
+        @Override
+        public boolean isRequiresRestart() {
             return nestedEntry.isRequiresRestart();
         }
+        
+        @Override
+        public void updateSelected(boolean isSelected) {
+            this.nestedEntry.updateSelected(isSelected);
+        }
+        
+        @Override
+        public boolean isEdited() {
+            return super.isEdited() || nestedEntry.isEdited();
+        }
     }
     
 }

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

@@ -16,6 +16,7 @@ import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -27,6 +28,7 @@ public class SelectionListEntry<T> extends TooltipListEntry<T> {
     
     private ImmutableList<T> values;
     private AtomicInteger index;
+    private final int original;
     private ButtonWidget buttonWidget, resetButton;
     private Consumer<T> saveConsumer;
     private Supplier<T> defaultValue;
@@ -62,14 +64,13 @@ public class SelectionListEntry<T> extends TooltipListEntry<T> {
         this.defaultValue = defaultValue;
         this.index = new AtomicInteger(this.values.indexOf(value));
         this.index.compareAndSet(-1, 0);
+        this.original = this.values.indexOf(value);
         this.buttonWidget = new ButtonWidget(0, 0, 150, 20, NarratorManager.EMPTY, widget -> {
             SelectionListEntry.this.index.incrementAndGet();
             SelectionListEntry.this.index.compareAndSet(SelectionListEntry.this.values.size(), 0);
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
             SelectionListEntry.this.index.set(getDefaultIndex());
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.saveConsumer = saveConsumer;
         this.widgets = Lists.newArrayList(buttonWidget, resetButton);
@@ -82,6 +83,11 @@ public class SelectionListEntry<T> extends TooltipListEntry<T> {
             saveConsumer.accept(getValue());
     }
     
+    @Override
+    public boolean isEdited() {
+        return super.isEdited() || !Objects.equals(this.index.get(), this.original);
+    }
+    
     @Override
     public T getValue() {
         return this.values.get(this.index.get());
@@ -101,12 +107,13 @@ public class SelectionListEntry<T> extends TooltipListEntry<T> {
         this.buttonWidget.active = isEditable();
         this.buttonWidget.y = y;
         this.buttonWidget.setMessage(nameProvider.apply(getValue()));
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.buttonWidget.x = x + resetButton.getWidth() + 2;
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.buttonWidget.x = x + entryWidth - 150;
         }

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

@@ -25,7 +25,6 @@ import java.util.Optional;
 public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigListEntry>> {
     
     private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
-    private Text categoryName;
     private List<AbstractConfigListEntry> entries;
     private CategoryLabelWidget widget;
     private List<Element> children;
@@ -34,7 +33,6 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
     @Deprecated
     public SubCategoryListEntry(Text categoryName, List<AbstractConfigListEntry> entries, boolean defaultExpanded) {
         super(categoryName, null);
-        this.categoryName = categoryName;
         this.entries = entries;
         this.expanded = defaultExpanded;
         this.widget = new CategoryLabelWidget();
@@ -56,7 +54,7 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
     }
     
     public Text getCategoryName() {
-        return categoryName;
+        return getFieldName();
     }
     
     @Override
@@ -80,7 +78,7 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
         DiffuseLighting.disable();
         RenderSystem.color4f(1, 1, 1, 1);
         drawTexture(matrices, x - 15, y + 4, 24, (widget.rectangle.contains(mouseX, mouseY) ? 18 : 0) + (expanded ? 9 : 0), 9, 9);
-        MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, categoryName, x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
+        MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getDisplayedFieldName(), x, y + 5, widget.rectangle.contains(mouseX, mouseY) ? 0xffe6fe16 : -1);
         for (AbstractConfigListEntry<?> entry : entries) {
             entry.setParent(getParent());
             entry.setScreen(getScreen());
@@ -101,6 +99,16 @@ public class SubCategoryListEntry extends TooltipListEntry<List<AbstractConfigLi
         }
     }
     
+    @Override
+    public boolean isEdited() {
+        for (AbstractConfigListEntry<?> entry : entries) {
+            if (entry.isEdited()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
     @Override
     public void lateRender(MatrixStack matrices, int mouseX, int mouseY, float delta) {
         if (expanded) {

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

@@ -60,17 +60,21 @@ public abstract class TextFieldListEntry<T> extends TooltipListEntry<T> {
         };
         textFieldWidget.setMaxLength(999999);
         textFieldWidget.setText(String.valueOf(original));
-        textFieldWidget.setChangedListener(s -> {
-            if (getScreen() != null && !original.equals(s))
-                getScreen().setEdited(true, isRequiresRestart());
-        });
         this.resetButton = new ButtonWidget(0, 0, MinecraftClient.getInstance().textRenderer.getWidth(resetButtonKey) + 6, 20, resetButtonKey, widget -> {
             TextFieldListEntry.this.textFieldWidget.setText(String.valueOf(defaultValue.get()));
-            getScreen().setEdited(true, isRequiresRestart());
         });
         this.widgets = Lists.newArrayList(textFieldWidget, resetButton);
     }
     
+    @Override
+    public boolean isEdited() {
+        return isChanged(original, textFieldWidget.getText());
+    }
+    
+    protected boolean isChanged(T original, String s) {
+        return !String.valueOf(original).equals(s);
+    }
+    
     protected static void setTextFieldWidth(TextFieldWidget widget, int width) {
         widget.setWidth(width);
     }
@@ -101,12 +105,13 @@ public abstract class TextFieldListEntry<T> extends TooltipListEntry<T> {
         this.resetButton.y = y;
         this.textFieldWidget.setEditable(isEditable());
         this.textFieldWidget.y = y + 1;
+        Text displayedFieldName = getDisplayedFieldName();
         if (MinecraftClient.getInstance().textRenderer.isRightToLeft()) {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(getFieldName()), y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, window.getScaledWidth() - x - MinecraftClient.getInstance().textRenderer.getWidth(displayedFieldName), y + 5, getPreferredTextColor());
             this.resetButton.x = x;
             this.textFieldWidget.x = x + resetButton.getWidth();
         } else {
-            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, getFieldName(), x, y + 5, getPreferredTextColor());
+            MinecraftClient.getInstance().textRenderer.drawWithShadow(matrices, displayedFieldName, x, y + 5, getPreferredTextColor());
             this.resetButton.x = x + entryWidth - resetButton.getWidth();
             this.textFieldWidget.x = x + entryWidth - 148;
         }

+ 2 - 15
src/main/java/me/shedaniel/clothconfig2/impl/ConfigBuilderImpl.java

@@ -9,7 +9,6 @@ import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.client.gui.DrawableHelper;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.resource.language.I18n;
 import net.minecraft.text.Text;
 import net.minecraft.text.TranslatableText;
 import net.minecraft.util.Identifier;
@@ -30,7 +29,6 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     private boolean editable = true;
     private boolean tabsSmoothScroll = true;
     private boolean listSmoothScroll = true;
-    private boolean doesProcessErrors = true;
     private boolean doesConfirmSave = true;
     private boolean transparentBackground = false;
     private Identifier defaultBackground = DrawableHelper.BACKGROUND_TEXTURE;
@@ -181,17 +179,6 @@ public class ConfigBuilderImpl implements ConfigBuilder {
         return doesConfirmSave;
     }
     
-    @Override
-    public ConfigBuilder setDoesProcessErrors(boolean processErrors) {
-        this.doesProcessErrors = processErrors;
-        return this;
-    }
-    
-    @Override
-    public boolean doesProcessErrors() {
-        return doesProcessErrors;
-    }
-    
     @Override
     public Identifier getDefaultBackgroundTexture() {
         return defaultBackground;
@@ -218,7 +205,7 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     public Screen build() {
         if (dataMap.isEmpty() || fallbackCategory == null)
             throw new NullPointerException("There cannot be no categories or fallback category!");
-        ClothConfigScreen screen = new ClothConfigScreen(parent, title, dataMap, doesConfirmSave, doesProcessErrors, listSmoothScroll, defaultBackground, categoryBackground) {
+        ClothConfigScreen screen = new ClothConfigScreen(parent, title, dataMap, doesConfirmSave, defaultBackground) {
             @Override
             public void save() {
                 if (savingRunnable != null)
@@ -233,9 +220,9 @@ public class ConfigBuilderImpl implements ConfigBuilder {
         };
         screen.setEditable(editable);
         screen.setFallbackCategory(fallbackCategory);
-        screen.setSmoothScrollingTabs(tabsSmoothScroll);
         screen.setTransparentBackground(transparentBackground);
         screen.setAlwaysShowTabs(alwaysShowTabs);
+        categoryBackground.forEach(screen::registerCategoryBackground);
         return screen;
     }
     

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

@@ -40,7 +40,7 @@ public class ConfigCategoryImpl implements ConfigCategory {
     
     @Override
     public ConfigCategory addEntry(AbstractConfigListEntry entry) {
-        listSupplier.get().add(new Pair<>(entry.getFieldName(), entry));
+        listSupplier.get().add(new Pair<>(null, entry));
         return this;
     }