Danielshe 5 vuotta sitten
vanhempi
sitoutus
8cfbeb520f

+ 1 - 1
gradle.properties

@@ -2,5 +2,5 @@ minecraft_version=19w42a
 yarn_version=19w42a+build.1
 fabric_loader_version=0.6.3+build.167
 fabric_version=0.4.6+build.251-1.15
-mod_version=2.2.1-unstable
+mod_version=2.3-unstable
 modmenu_version=1.7.14-unstable.19w42a+build.10

+ 1 - 0
src/main/java/me/shedaniel/clothconfig2/ClothConfigInitializer.java

@@ -136,6 +136,7 @@ public class ClothConfigInitializer implements ClientModInitializer {
                         builder.setSavingRunnable(() -> {
                             saveConfig();
                         });
+                        builder.transparentBackground();
                         MinecraftClient.getInstance().openScreen(builder.build());
                     } catch (Throwable throwable) {
                         throwable.printStackTrace();

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

@@ -72,7 +72,30 @@ public interface ConfigBuilder {
     
     ConfigBuilder setAfterInitConsumer(Consumer<Screen> afterInitConsumer);
     
+    default ConfigBuilder alwaysShowTabs() {
+        return setAlwaysShowTabs(true);
+    }
+    
+    boolean isAlwaysShowTabs();
+    
+    ConfigBuilder setAlwaysShowTabs(boolean alwaysShowTabs);
+    
+    ConfigBuilder setTransparentBackground(boolean transparentBackground);
+    
+    default ConfigBuilder transparentBackground() {
+        return setTransparentBackground(true);
+    }
+    
+    default ConfigBuilder solidBackground() {
+        return setTransparentBackground(false);
+    }
+    
+    @Deprecated
     default ConfigEntryBuilderImpl getEntryBuilder() {
+        return (ConfigEntryBuilderImpl) entryBuilder();
+    }
+    
+    default ConfigEntryBuilder entryBuilder() {
         return ConfigEntryBuilderImpl.create();
     }
     

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

@@ -15,4 +15,6 @@ public interface ConfigCategory {
     
     ConfigCategory setCategoryBackground(Identifier identifier);
     
+    void removeCategory();
+    
 }

+ 20 - 0
src/main/java/me/shedaniel/clothconfig2/api/ScissorsHandler.java

@@ -0,0 +1,20 @@
+package me.shedaniel.clothconfig2.api;
+
+import me.shedaniel.clothconfig2.impl.ScissorsHandlerImpl;
+import me.shedaniel.math.api.Rectangle;
+
+import java.util.List;
+
+public interface ScissorsHandler {
+    ScissorsHandler INSTANCE = ScissorsHandlerImpl.INSTANCE;
+    
+    void clearScissors();
+    
+    List<Rectangle> getScissorsAreas();
+    
+    void scissor(Rectangle rectangle);
+    
+    void removeLastScissor();
+    
+    void applyScissors();
+}

+ 185 - 133
src/main/java/me/shedaniel/clothconfig2/gui/ClothConfigScreen.java

@@ -8,11 +8,11 @@ import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
 import me.shedaniel.clothconfig2.api.AbstractConfigEntry;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.QueuedTooltip;
+import me.shedaniel.clothconfig2.api.ScissorsHandler;
 import me.shedaniel.clothconfig2.gui.widget.DynamicElementListWidget;
 import me.shedaniel.math.api.Rectangle;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawableHelper;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.ConfirmScreen;
 import net.minecraft.client.gui.screen.Screen;
@@ -23,21 +23,21 @@ import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.Tessellator;
 import net.minecraft.client.render.VertexFormats;
 import net.minecraft.client.resource.language.I18n;
-import net.minecraft.client.util.Window;
 import net.minecraft.text.LiteralText;
 import net.minecraft.text.TranslatableText;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.Pair;
 import net.minecraft.util.Tickable;
 import net.minecraft.util.math.MathHelper;
-import org.lwjgl.opengl.GL11;
 
+import javax.annotation.Nullable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
+@SuppressWarnings("deprecation")
 public abstract class ClothConfigScreen extends Screen {
     
     private static final Identifier CONFIG_TEX = new Identifier("cloth-config2", "textures/gui/cloth_config.png");
@@ -53,10 +53,7 @@ public abstract class ClothConfigScreen extends Screen {
     private boolean edited;
     private boolean requiresRestart;
     private boolean confirmSave;
-    private AbstractButtonWidget buttonQuit;
-    private AbstractButtonWidget buttonSave;
-    private AbstractButtonWidget buttonLeftTab;
-    private AbstractButtonWidget buttonRightTab;
+    private AbstractButtonWidget buttonQuit, buttonSave, buttonLeftTab, buttonRightTab;
     private Rectangle tabsBounds, tabsLeftBounds, tabsRightBounds;
     private String title;
     private double tabsMaximumScrolled = -1d;
@@ -66,20 +63,12 @@ public abstract class ClothConfigScreen extends Screen {
     private boolean smoothScrollingList = true;
     private Identifier defaultBackgroundLocation;
     private Map<String, Identifier> categoryBackgroundLocation;
+    private boolean transparentBackground = false;
+    private boolean editable = true;
+    @Nullable private String defaultFallbackCategory = null;
+    private boolean alwaysShowTabs = false;
     
-    public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o) {
-        this(parent, title, o, true, true);
-    }
-    
-    public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o, boolean confirmSave, boolean displayErrors) {
-        this(parent, title, o, confirmSave, displayErrors, true, DrawableHelper.BACKGROUND_LOCATION);
-    }
-    
-    public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o, boolean confirmSave, boolean displayErrors, boolean smoothScrollingList, Identifier defaultBackgroundLocation) {
-        this(parent, title, o, confirmSave, displayErrors, smoothScrollingList, defaultBackgroundLocation, Maps.newHashMap());
-    }
-    
-    @SuppressWarnings("deprecation")
+    @Deprecated
     public ClothConfigScreen(Screen parent, String title, Map<String, List<Pair<String, Object>>> o, boolean confirmSave, boolean displayErrors, boolean smoothScrollingList, Identifier defaultBackgroundLocation, Map<String, Identifier> categoryBackgroundLocation) {
         super(new LiteralText(""));
         this.parent = parent;
@@ -120,10 +109,39 @@ public abstract class ClothConfigScreen extends Screen {
         this.categoryBackgroundLocation = categoryBackgroundLocation;
     }
     
+    public boolean isShowingTabs() {
+        return isAlwaysShowTabs() || tabs.size() > 1;
+    }
+    
+    public boolean isAlwaysShowTabs() {
+        return alwaysShowTabs;
+    }
+    
+    @Deprecated
+    public void setAlwaysShowTabs(boolean alwaysShowTabs) {
+        this.alwaysShowTabs = alwaysShowTabs;
+    }
+    
+    public boolean isTransparentBackground() {
+        return transparentBackground && MinecraftClient.getInstance().world != null;
+    }
+    
+    @Deprecated
+    public void setTransparentBackground(boolean transparentBackground) {
+        this.transparentBackground = transparentBackground;
+    }
+    
     public String getFallbackCategory() {
+        if (defaultFallbackCategory != null)
+            return defaultFallbackCategory;
         return tabs.get(0).getLeft();
     }
     
+    @Deprecated
+    public void setFallbackCategory(@Nullable String defaultFallbackCategory) {
+        this.defaultFallbackCategory = defaultFallbackCategory;
+    }
+    
     @Override
     public void tick() {
         super.tick();
@@ -142,6 +160,7 @@ public abstract class ClothConfigScreen extends Screen {
         return smoothScrollingList;
     }
     
+    @Deprecated
     public void setSmoothScrollingList(boolean smoothScrollingList) {
         this.smoothScrollingList = smoothScrollingList;
     }
@@ -150,6 +169,7 @@ public abstract class ClothConfigScreen extends Screen {
         return smoothScrollingTabs;
     }
     
+    @Deprecated
     public void setSmoothScrollingTabs(boolean smoothScrolling) {
         this.smoothScrollingTabs = smoothScrolling;
     }
@@ -165,7 +185,6 @@ public abstract class ClothConfigScreen extends Screen {
         buttonSave.active = edited;
     }
     
-    @SuppressWarnings("deprecation")
     public void setEdited(boolean edited, boolean requiresRestart) {
         setEdited(edited);
         if (!this.requiresRestart && requiresRestart)
@@ -180,7 +199,7 @@ public abstract class ClothConfigScreen extends Screen {
         if (listWidget != null)
             tabbedEntries.put(tabs.get(selectedTabIndex).getLeft(), (List) listWidget.children());
         selectedTabIndex = nextTabIndex;
-        children.add(listWidget = new ListWidget(minecraft, width, height, 70, height - 32, getBackgroundLocation()));
+        children.add(listWidget = new ListWidget(minecraft, 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));
@@ -193,15 +212,10 @@ public abstract class ClothConfigScreen extends Screen {
         addButton(buttonSave = new AbstractPressableButtonWidget(width / 2 + 4, height - 26, 150, 20, "") {
             @Override
             public void onPress() {
-                Map<String, List<Pair<String, Object>>> map = Maps.newLinkedHashMap();
-                tabbedEntries.forEach((s, abstractListEntries) -> {
-                    List list = abstractListEntries.stream().map(entry -> new Pair(entry.getFieldName(), entry.getValue())).collect(Collectors.toList());
-                    map.put(s, list);
-                });
                 for(List<AbstractConfigEntry> entries : Lists.newArrayList(tabbedEntries.values()))
                     for(AbstractConfigEntry entry : entries)
                         entry.save();
-                onSave(map);
+                save();
                 if (requiresRestart)
                     ClothConfigScreen.this.minecraft.openScreen(new ClothRequiresRestartScreen(parent));
                 else
@@ -227,53 +241,57 @@ public abstract class ClothConfigScreen extends Screen {
             }
         });
         buttonSave.active = edited;
-        tabsBounds = new Rectangle(0, 41, width, 24);
-        tabsLeftBounds = new Rectangle(0, 41, 18, 24);
-        tabsRightBounds = new Rectangle(width - 18, 41, 18, 24);
-        children.add(buttonLeftTab = new AbstractPressableButtonWidget(4, 44, 12, 18, "") {
-            @Override
-            public void onPress() {
-                tabsScrollProgress = Integer.MIN_VALUE;
-                tabsScrollVelocity = 0d;
-                clampTabsScrolled();
-            }
-            
-            @Override
-            public void renderButton(int int_1, int int_2, float float_1) {
-                minecraft.getTextureManager().bindTexture(CONFIG_TEX);
-                RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
-                int int_3 = this.getYImage(this.isHovered());
-                RenderSystem.enableBlend();
-                RenderSystem.blendFuncSeparate(770, 771, 0, 1);
-                RenderSystem.blendFunc(770, 771);
-                this.blit(x, y, 12, 18 * int_3, width, height);
+        if (isShowingTabs()) {
+            tabsBounds = new Rectangle(0, 41, width, 24);
+            tabsLeftBounds = new Rectangle(0, 41, 18, 24);
+            tabsRightBounds = new Rectangle(width - 18, 41, 18, 24);
+            children.add(buttonLeftTab = new AbstractPressableButtonWidget(4, 44, 12, 18, "") {
+                @Override
+                public void onPress() {
+                    tabsScrollProgress = Integer.MIN_VALUE;
+                    tabsScrollVelocity = 0d;
+                    clampTabsScrolled();
+                }
+                
+                @Override
+                public void renderButton(int int_1, int int_2, float float_1) {
+                    minecraft.getTextureManager().bindTexture(CONFIG_TEX);
+                    RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
+                    int int_3 = this.getYImage(this.isHovered());
+                    RenderSystem.enableBlend();
+                    RenderSystem.blendFuncSeparate(770, 771, 0, 1);
+                    RenderSystem.blendFunc(770, 771);
+                    this.blit(x, y, 12, 18 * int_3, width, height);
+                }
+            });
+            int j = 0;
+            for(Pair<String, Integer> tab : tabs) {
+                tabButtons.add(new ClothConfigTabButton(this, j, -100, 43, tab.getRight(), 20, I18n.translate(tab.getLeft())));
+                j++;
             }
-        });
-        int j = 0;
-        for(Pair<String, Integer> tab : tabs) {
-            tabButtons.add(new ClothConfigTabButton(this, j, -100, 43, tab.getRight(), 20, I18n.translate(tab.getLeft())));
-            j++;
+            tabButtons.forEach(children::add);
+            children.add(buttonRightTab = new AbstractPressableButtonWidget(width - 16, 44, 12, 18, "") {
+                @Override
+                public void onPress() {
+                    tabsScrollProgress = Integer.MAX_VALUE;
+                    tabsScrollVelocity = 0d;
+                    clampTabsScrolled();
+                }
+                
+                @Override
+                public void renderButton(int int_1, int int_2, float float_1) {
+                    minecraft.getTextureManager().bindTexture(CONFIG_TEX);
+                    RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
+                    int int_3 = this.getYImage(this.isHovered());
+                    RenderSystem.enableBlend();
+                    RenderSystem.blendFuncSeparate(770, 771, 0, 1);
+                    RenderSystem.blendFunc(770, 771);
+                    this.blit(x, y, 0, 18 * int_3, width, height);
+                }
+            });
+        } else {
+            tabsBounds = tabsLeftBounds = tabsRightBounds = new Rectangle();
         }
-        tabButtons.forEach(children::add);
-        children.add(buttonRightTab = new AbstractPressableButtonWidget(width - 16, 44, 12, 18, "") {
-            @Override
-            public void onPress() {
-                tabsScrollProgress = Integer.MAX_VALUE;
-                tabsScrollVelocity = 0d;
-                clampTabsScrolled();
-            }
-            
-            @Override
-            public void renderButton(int int_1, int int_2, float float_1) {
-                minecraft.getTextureManager().bindTexture(CONFIG_TEX);
-                RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
-                int int_3 = this.getYImage(this.isHovered());
-                RenderSystem.enableBlend();
-                RenderSystem.blendFuncSeparate(770, 771, 0, 1);
-                RenderSystem.blendFunc(770, 771);
-                this.blit(x, y, 0, 18 * int_3, width, height);
-            }
-        });
     }
     
     @Override
@@ -294,7 +312,7 @@ public abstract class ClothConfigScreen extends Screen {
             tabs.forEach(pair -> d.addAndGet(pair.getRight() + 2));
             tabsMaximumScrolled = d.get();
         }
-        return tabsMaximumScrolled;
+        return tabsMaximumScrolled + 8;
     }
     
     public void resetTabsMaximumScrolled() {
@@ -314,53 +332,58 @@ public abstract class ClothConfigScreen extends Screen {
     
     @Override
     public void render(int int_1, int int_2, float float_1) {
-        if (smoothScrollingTabs) {
-            double change = tabsScrollVelocity * 0.2f;
-            if (change != 0) {
-                if (change > 0 && change < .2)
-                    change = .2;
-                else if (change < 0 && change > -.2)
-                    change = -.2;
-                tabsScrollProgress += change;
-                tabsScrollVelocity -= change;
-                if (change > 0 == tabsScrollVelocity < 0)
-                    tabsScrollVelocity = 0f;
+        if (isShowingTabs()) {
+            if (smoothScrollingTabs) {
+                double change = tabsScrollVelocity * 0.2f;
+                if (change != 0) {
+                    if (change > 0 && change < .2)
+                        change = .2;
+                    else if (change < 0 && change > -.2)
+                        change = -.2;
+                    tabsScrollProgress += change;
+                    tabsScrollVelocity -= change;
+                    if (change > 0 == tabsScrollVelocity < 0)
+                        tabsScrollVelocity = 0f;
+                    clampTabsScrolled();
+                }
+            } else {
+                tabsScrollProgress += tabsScrollVelocity;
+                tabsScrollVelocity = 0d;
                 clampTabsScrolled();
             }
-        } else {
-            tabsScrollProgress += tabsScrollVelocity;
-            tabsScrollVelocity = 0d;
-            clampTabsScrolled();
+            int xx = 24 - (int) tabsScrollProgress;
+            for(ClothConfigTabButton tabButton : tabButtons) {
+                tabButton.x = xx;
+                xx += tabButton.getWidth() + 2;
+            }
+            buttonLeftTab.active = tabsScrollProgress > 0d;
+            buttonRightTab.active = tabsScrollProgress < getTabsMaximumScrolled() - width + 40;
         }
-        int xx = 20 - (int) tabsScrollProgress;
-        for(ClothConfigTabButton tabButton : tabButtons) {
-            tabButton.x = xx;
-            xx += tabButton.getWidth() + 2;
+        if (isTransparentBackground()) {
+            fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
+        } else {
+            renderDirtBackground(0);
         }
-        buttonLeftTab.active = tabsScrollProgress > 0d;
-        buttonRightTab.active = tabsScrollProgress < getTabsMaximumScrolled() - width + 40;
-        renderDirtBackground(0);
         listWidget.render(int_1, int_2, float_1);
-        Window window = minecraft.getWindow();
-        int sw = window.getWidth();
-        int sh = window.getHeight();
-        int x = Math.round(sw * (listWidget.left / (float) width));
-        int y = Math.round(sh * (listWidget.top / (float) height));
-        int ww = Math.round(sw * (listWidget.width / (float) width));
-        int hh = Math.round(sh * ((listWidget.bottom - listWidget.top) / (float) height));
-        GL11.glEnable(GL11.GL_SCISSOR_TEST);
-        GL11.glScissor(x, sh - hh - y, ww, hh);
+        ScissorsHandler.INSTANCE.scissor(new Rectangle(listWidget.left, listWidget.top, listWidget.width, listWidget.bottom - listWidget.top));
         for(AbstractConfigEntry child : listWidget.children())
             child.lateRender(int_1, int_2, float_1);
-        GL11.glDisable(GL11.GL_SCISSOR_TEST);
-        overlayBackground(tabsBounds, 32, 32, 32, 255, 255);
-        drawCenteredString(minecraft.textRenderer, title, width / 2, 18, -1);
-        tabButtons.forEach(widget -> widget.render(int_1, int_2, float_1));
-        overlayBackground(tabsLeftBounds, 64, 64, 64, 255, 255);
-        overlayBackground(tabsRightBounds, 64, 64, 64, 255, 255);
-        drawShades();
-        buttonLeftTab.render(int_1, int_2, float_1);
-        buttonRightTab.render(int_1, int_2, float_1);
+        ScissorsHandler.INSTANCE.removeLastScissor();
+        if (isShowingTabs()) {
+            drawCenteredString(minecraft.textRenderer, title, width / 2, 18, -1);
+            Rectangle onlyInnerTabBounds = new Rectangle(tabsBounds.x + 20, tabsBounds.y, tabsBounds.width - 40, tabsBounds.height);
+            ScissorsHandler.INSTANCE.scissor(onlyInnerTabBounds);
+            if (isTransparentBackground())
+                fillGradient(onlyInnerTabBounds.x, onlyInnerTabBounds.y, onlyInnerTabBounds.getMaxX(), onlyInnerTabBounds.getMaxY(), 0x68000000, 0x68000000);
+            else
+                overlayBackground(onlyInnerTabBounds, 32, 32, 32, 255, 255);
+            tabButtons.forEach(widget -> widget.render(int_1, int_2, float_1));
+            drawTabsShades(0, isTransparentBackground() ? 120 : 255);
+            ScissorsHandler.INSTANCE.removeLastScissor();
+            buttonLeftTab.render(int_1, int_2, float_1);
+            buttonRightTab.render(int_1, int_2, float_1);
+        } else
+            drawCenteredString(minecraft.textRenderer, title, width / 2, 12, -1);
         
         if (displayErrors && isEditable()) {
             List<String> errors = Lists.newArrayList();
@@ -371,17 +394,24 @@ public abstract class ClothConfigScreen extends Screen {
             if (errors.size() > 0) {
                 minecraft.getTextureManager().bindTexture(CONFIG_TEX);
                 RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+                String text = "§c" + (errors.size() == 1 ? errors.get(0) : I18n.translate("text.cloth-config.multi_error"));
+                if (isTransparentBackground()) {
+                    int stringWidth = minecraft.textRenderer.getStringWidth(text);
+                    fillGradient(8, 9, 20 + stringWidth, 14 + minecraft.textRenderer.fontHeight, 0x68000000, 0x68000000);
+                }
                 blit(10, 10, 0, 54, 3, 11);
-                if (errors.size() == 1)
-                    drawString(minecraft.textRenderer, "§c" + errors.get(0), 18, 12, -1);
-                else
-                    drawString(minecraft.textRenderer, "§c" + I18n.translate("text.cloth-config.multi_error"), 18, 12, -1);
+                drawString(minecraft.textRenderer, text, 18, 12, -1);
             }
         } else if (!isEditable()) {
             minecraft.getTextureManager().bindTexture(CONFIG_TEX);
             RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+            String text = "§c" + I18n.translate("text.cloth-config.not_editable");
+            if (isTransparentBackground()) {
+                int stringWidth = minecraft.textRenderer.getStringWidth(text);
+                fillGradient(8, 9, 20 + stringWidth, 14 + minecraft.textRenderer.fontHeight, 0x68000000, 0x68000000);
+            }
             blit(10, 10, 0, 54, 3, 11);
-            drawString(minecraft.textRenderer, "§c" + I18n.translate("text.cloth-config.not_editable"), 18, 12, -1);
+            drawString(minecraft.textRenderer, text, 18, 12, -1);
         }
         super.render(int_1, int_2, float_1);
         queuedTooltips.forEach(queuedTooltip -> renderTooltip(queuedTooltip.getText(), queuedTooltip.getX(), queuedTooltip.getY()));
@@ -392,8 +422,7 @@ public abstract class ClothConfigScreen extends Screen {
         queuedTooltips.add(queuedTooltip);
     }
     
-    @SuppressWarnings("deprecation")
-    private void drawShades() {
+    private void drawTabsShades(int lightColor, int darkColor) {
         RenderSystem.enableBlend();
         RenderSystem.blendFuncSeparate(770, 771, 0, 1);
         RenderSystem.disableAlphaTest();
@@ -402,16 +431,16 @@ public abstract class ClothConfigScreen extends Screen {
         Tessellator tessellator = Tessellator.getInstance();
         BufferBuilder buffer = tessellator.getBufferBuilder();
         buffer.begin(7, VertexFormats.POSITION_UV_COLOR);
-        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMinY() + 4, 0.0D).texture(0, 1).color(0, 0, 0, 0).next();
-        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMinY() + 4, 0.0D).texture(1, 1).color(0, 0, 0, 0).next();
-        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMinY(), 0.0D).texture(1, 0).color(0, 0, 0, 255).next();
-        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMinY(), 0.0D).texture(0, 0).color(0, 0, 0, 255).next();
+        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMinY() + 4, 0.0D).texture(0, 1f).color(0, 0, 0, lightColor).next();
+        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMinY() + 4, 0.0D).texture(1f, 1f).color(0, 0, 0, lightColor).next();
+        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMinY(), 0.0D).texture(1f, 0).color(0, 0, 0, darkColor).next();
+        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMinY(), 0.0D).texture(0, 0).color(0, 0, 0, darkColor).next();
         tessellator.draw();
         buffer.begin(7, VertexFormats.POSITION_UV_COLOR);
-        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMaxY(), 0.0D).texture(0, 1).color(0, 0, 0, 255).next();
-        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMaxY(), 0.0D).texture(1, 1).color(0, 0, 0, 255).next();
-        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMaxY() - 4, 0.0D).texture(1, 0).color(0, 0, 0, 0).next();
-        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMaxY() - 4, 0.0D).texture(0, 0).color(0, 0, 0, 0).next();
+        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMaxY(), 0.0D).texture(0, 1f).color(0, 0, 0, darkColor).next();
+        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMaxY(), 0.0D).texture(1f, 1f).color(0, 0, 0, darkColor).next();
+        buffer.vertex(tabsBounds.getMaxX() - 20, tabsBounds.getMaxY() - 4, 0.0D).texture(1f, 0).color(0, 0, 0, lightColor).next();
+        buffer.vertex(tabsBounds.getMinX() + 20, tabsBounds.getMaxY() - 4, 0.0D).texture(0, 0).color(0, 0, 0, lightColor).next();
         tessellator.draw();
         RenderSystem.enableTexture();
         RenderSystem.shadeModel(7424);
@@ -421,6 +450,8 @@ public abstract class ClothConfigScreen extends Screen {
     
     @SuppressWarnings("deprecation")
     protected void overlayBackground(Rectangle rect, int red, int green, int blue, int startAlpha, int endAlpha) {
+        if (isTransparentBackground())
+            return;
         Tessellator tessellator = Tessellator.getInstance();
         BufferBuilder buffer = tessellator.getBufferBuilder();
         minecraft.getTextureManager().bindTexture(getBackgroundLocation());
@@ -446,10 +477,16 @@ public abstract class ClothConfigScreen extends Screen {
         return super.keyPressed(int_1, int_2, int_3);
     }
     
-    public abstract void onSave(Map<String, List<Pair<String, Object>>> o);
+    public void save() {
+    }
     
     public boolean isEditable() {
-        return true;
+        return editable;
+    }
+    
+    @Deprecated
+    public void setEditable(boolean editable) {
+        this.editable = editable;
     }
     
     private class QuitSaveConsumer implements BooleanConsumer {
@@ -487,7 +524,7 @@ public abstract class ClothConfigScreen extends Screen {
         protected final void clearStuff() {
             this.clearItems();
         }
-    
+        
         @Override
         public boolean mouseClicked(double double_1, double double_2, int int_1) {
             this.updateScrollingState(double_1, double_2, int_1);
@@ -505,10 +542,25 @@ public abstract class ClothConfigScreen extends Screen {
                     this.clickedHeader((int) (double_1 - (double) (this.left + this.width / 2 - this.getItemWidth() / 2)), (int) (double_2 - (double) this.top) + (int) this.getScroll() - 4);
                     return true;
                 }
-        
+                
                 return this.scrolling;
             }
         }
+        
+        @Override
+        protected void renderBackBackground(BufferBuilder buffer, Tessellator tessellator) {
+            if (!isTransparentBackground())
+                super.renderBackBackground(buffer, tessellator);
+            else {
+                fillGradient(left, top, right, bottom, 0x68000000, 0x68000000);
+            }
+        }
+        
+        @Override
+        protected void renderHoleBackground(int int_1, int int_2, int int_3, int int_4) {
+            if (!isTransparentBackground())
+                super.renderHoleBackground(int_1, int_2, int_3, int_4);
+        }
     }
     
 }

+ 13 - 30
src/main/java/me/shedaniel/clothconfig2/gui/entries/DropdownBoxEntry.java

@@ -5,6 +5,7 @@ import com.google.common.collect.Lists;
 import com.mojang.blaze3d.systems.RenderSystem;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
 import me.shedaniel.clothconfig2.api.RunSixtyTimesEverySec;
+import me.shedaniel.clothconfig2.api.ScissorsHandler;
 import me.shedaniel.clothconfig2.gui.widget.DynamicEntryListWidget.SmoothScrollingSettings;
 import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget.Interpolation;
 import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget.Precision;
@@ -23,7 +24,6 @@ import net.minecraft.client.render.VertexFormats;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.util.Window;
 import net.minecraft.util.math.MathHelper;
-import org.lwjgl.opengl.GL11;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -369,40 +369,24 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
             fill(lastRectangle.x, lastRectangle.y + lastRectangle.height, lastRectangle.x + cWidth, lastRectangle.y + lastRectangle.height + last10Height + 1, -6250336);
             fill(lastRectangle.x + 1, lastRectangle.y + lastRectangle.height + 1, lastRectangle.x + cWidth - 1, lastRectangle.y + lastRectangle.height + last10Height, -16777216);
             RenderSystem.pushMatrix();
-            Window window = MinecraftClient.getInstance().getWindow();
-            int sw = window.getWidth();
-            int sh = window.getHeight();
-            float dw = window.getScaledWidth();
-            float dh = window.getScaledHeight();
-            {
-                int yyy = Math.max(lastRectangle.y + lastRectangle.height + 1, getEntry().getParent().top);
-                int x = Math.round(sw * (lastRectangle.x / dw));
-                int y = Math.round(sh * (yyy / dh));
-                int ww = Math.round(sw * ((cWidth - 6) / dw));
-                int hh = Math.round(sh * ((Math.min(last10Height - 1 - yyy + (lastRectangle.y + lastRectangle.height + 1), getEntry().getParent().bottom - yyy)) / dh));
-                GL11.glEnable(GL11.GL_SCISSOR_TEST);
-                GL11.glScissor(x, sh - hh - y, ww, hh);
-                double yy = lastRectangle.y + lastRectangle.height - scroll;
-                for(SelectionCellElement<R> cell : currentElements) {
-                    if (yy + getCellCreator().getCellHeight() >= lastRectangle.y + lastRectangle.height && yy <= lastRectangle.y + lastRectangle.height + last10Height + 1)
-                        cell.render(mouseX, mouseY, lastRectangle.x, (int) yy, getMaxScrollPosition() > 6 ? getCellCreator().getCellWidth() - 6 : getCellCreator().getCellWidth(), getCellCreator().getCellHeight(), delta);
-                    else
-                        cell.dontRender(delta);
-                    yy += getCellCreator().getCellHeight();
-                }
-                GL11.glDisable(GL11.GL_SCISSOR_TEST);
+            
+            ScissorsHandler.INSTANCE.scissor(new Rectangle(lastRectangle.x, lastRectangle.y + lastRectangle.height + 1, cWidth - 6, last10Height - 1));
+            double yy = lastRectangle.y + lastRectangle.height - scroll;
+            for(SelectionCellElement<R> cell : currentElements) {
+                if (yy + getCellCreator().getCellHeight() >= lastRectangle.y + lastRectangle.height && yy <= lastRectangle.y + lastRectangle.height + last10Height + 1)
+                    cell.render(mouseX, mouseY, lastRectangle.x, (int) yy, getMaxScrollPosition() > 6 ? getCellCreator().getCellWidth() - 6 : getCellCreator().getCellWidth(), getCellCreator().getCellHeight(), delta);
+                else
+                    cell.dontRender(delta);
+                yy += getCellCreator().getCellHeight();
             }
+            ScissorsHandler.INSTANCE.removeLastScissor();
+            
             if (currentElements.isEmpty()) {
                 TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
                 String s = I18n.translate("text.cloth-config.dropdown.value.unknown");
                 textRenderer.drawWithShadow(s, lastRectangle.x + getCellCreator().getCellWidth() / 2 - textRenderer.getStringWidth(s) / 2, lastRectangle.y + lastRectangle.height + 3, -1);
             }
-            {
-                int y = Math.round(sh * (getEntry().getParent().top / dh));
-                int hh = Math.round(sh * ((getEntry().getParent().bottom - getEntry().getParent().top) / dh));
-                GL11.glEnable(GL11.GL_SCISSOR_TEST);
-                GL11.glScissor(0, sh - hh - y, sw, hh);
-            }
+            
             if (getMaxScrollPosition() > 6) {
                 RenderSystem.disableTexture();
                 int scrollbarPositionMinX = lastRectangle.x + getCellCreator().getCellWidth() - 6;
@@ -436,7 +420,6 @@ public class DropdownBoxEntry<T> extends TooltipListEntry<T> {
                 tessellator.draw();
                 RenderSystem.enableTexture();
             }
-            GL11.glDisable(GL11.GL_SCISSOR_TEST);
             RenderSystem.popMatrix();
         }
         

+ 17 - 22
src/main/java/me/shedaniel/clothconfig2/gui/widget/DynamicEntryListWidget.java

@@ -2,6 +2,8 @@ package me.shedaniel.clothconfig2.gui.widget;
 
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.clothconfig2.api.ScissorsHandler;
+import me.shedaniel.math.api.Rectangle;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.client.MinecraftClient;
@@ -13,10 +15,8 @@ import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.client.render.Tessellator;
 import net.minecraft.client.render.VertexFormats;
-import net.minecraft.client.util.Window;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
-import org.lwjgl.opengl.GL11;
 
 import java.util.AbstractList;
 import java.util.ArrayList;
@@ -165,14 +165,8 @@ public abstract class DynamicEntryListWidget<E extends DynamicEntryListWidget.En
     protected void renderDecorations(int int_1, int int_2) {
     }
     
-    public void render(int mouseX, int mouseY, float delta) {
-        this.drawBackground();
-        int scrollbarPosition = this.getScrollbarPosition();
-        int int_4 = scrollbarPosition + 6;
-        RenderSystem.disableLighting();
-        RenderSystem.disableFog();
-        Tessellator tessellator = Tessellator.getInstance();
-        BufferBuilder buffer = tessellator.getBufferBuilder();
+    @Deprecated
+    protected void renderBackBackground(BufferBuilder buffer, Tessellator tessellator) {
         this.client.getTextureManager().bindTexture(backgroundLocation);
         RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
         float float_2 = 32.0F;
@@ -182,23 +176,24 @@ public abstract class DynamicEntryListWidget<E extends DynamicEntryListWidget.En
         buffer.vertex(this.right, this.top, 0.0D).texture(this.right / 32.0F, ((this.top + (int) this.getScroll()) / 32.0F)).color(32, 32, 32, 255).next();
         buffer.vertex(this.left, this.top, 0.0D).texture(this.left / 32.0F, ((this.top + (int) this.getScroll()) / 32.0F)).color(32, 32, 32, 255).next();
         tessellator.draw();
+    }
+    
+    public void render(int mouseX, int mouseY, float delta) {
+        this.drawBackground();
+        int scrollbarPosition = this.getScrollbarPosition();
+        int int_4 = scrollbarPosition + 6;
+        RenderSystem.disableLighting();
+        RenderSystem.disableFog();
+        Tessellator tessellator = Tessellator.getInstance();
+        BufferBuilder buffer = tessellator.getBufferBuilder();
+        renderBackBackground(buffer, tessellator);
         int rowLeft = this.getRowLeft();
         int startY = this.top + 4 - (int) this.getScroll();
         if (this.renderSelection)
             this.renderHeader(rowLeft, startY, tessellator);
-        Window window = client.getWindow();
-        int sw = window.getWidth();
-        int sh = window.getHeight();
-        float dw = window.getScaledWidth();
-        float dh = window.getScaledHeight();
-        int x = Math.round(sw * (this.left / dw));
-        int y = Math.round(sh * (this.top / dh));
-        int ww = Math.round(sw * (this.width / dw));
-        int hh = Math.round(sh * (this.height / dh));
-        GL11.glEnable(GL11.GL_SCISSOR_TEST);
-        GL11.glScissor(x, sh - hh - y, ww, hh);
+        ScissorsHandler.INSTANCE.scissor(new Rectangle(left, top, width, bottom - top));
         this.renderList(rowLeft, startY, mouseX, mouseY, delta);
-        GL11.glDisable(GL11.GL_SCISSOR_TEST);
+        ScissorsHandler.INSTANCE.removeLastScissor();
         RenderSystem.disableDepthTest();
         this.renderHoleBackground(0, this.top, 255, 255);
         this.renderHoleBackground(this.bottom, this.height, 255, 255);

+ 1 - 0
src/main/java/me/shedaniel/clothconfig2/gui/widget/DynamicSmoothScrollingEntryListWidget.java

@@ -8,6 +8,7 @@ import net.minecraft.client.render.VertexFormats;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 
+@Deprecated
 public abstract class DynamicSmoothScrollingEntryListWidget<E extends DynamicEntryListWidget.Entry<E>> extends DynamicEntryListWidget<E> {
     
     protected double scrollVelocity;

+ 34 - 13
src/main/java/me/shedaniel/clothconfig2/impl/ConfigBuilderImpl.java

@@ -16,6 +16,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
 
+@Deprecated
 public class ConfigBuilderImpl implements ConfigBuilder {
     
     private Runnable savingRunnable;
@@ -26,17 +27,36 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     private boolean listSmoothScroll = true;
     private boolean doesProcessErrors = true;
     private boolean doesConfirmSave = true;
+    private boolean transparentBackground = false;
     private Identifier defaultBackground = DrawableHelper.BACKGROUND_LOCATION;
     private Consumer<Screen> afterInitConsumer = screen -> {};
     private Map<String, Identifier> categoryBackground = Maps.newHashMap();
     private Map<String, List<Pair<String, Object>>> dataMap = Maps.newLinkedHashMap();
     private String fallbackCategory = null;
+    private boolean alwaysShowTabs = false;
     
     @Deprecated
     public ConfigBuilderImpl() {
     
     }
     
+    @Override
+    public boolean isAlwaysShowTabs() {
+        return alwaysShowTabs;
+    }
+    
+    @Override
+    public ConfigBuilder setAlwaysShowTabs(boolean alwaysShowTabs) {
+        this.alwaysShowTabs = alwaysShowTabs;
+        return this;
+    }
+    
+    @Override
+    public ConfigBuilder setTransparentBackground(boolean transparentBackground) {
+        this.transparentBackground = transparentBackground;
+        return this;
+    }
+    
     @Override
     public ConfigBuilder setAfterInitConsumer(Consumer<Screen> afterInitConsumer) {
         this.afterInitConsumer = afterInitConsumer;
@@ -86,20 +106,26 @@ public class ConfigBuilderImpl implements ConfigBuilder {
     public ConfigCategory getOrCreateCategory(String categoryKey) {
         if (dataMap.containsKey(categoryKey))
             return new ConfigCategoryImpl(categoryKey, identifier -> {
+                if (transparentBackground)
+                    throw new IllegalStateException("Cannot set category background if screen is using transparent background.");
                 categoryBackground.put(categoryKey, identifier);
-            }, () -> dataMap.get(categoryKey));
+            }, () -> dataMap.get(categoryKey), () -> removeCategory(categoryKey));
         dataMap.put(categoryKey, Lists.newArrayList());
         if (fallbackCategory == null)
             fallbackCategory = categoryKey;
         return new ConfigCategoryImpl(categoryKey, identifier -> {
+            if (transparentBackground)
+                throw new IllegalStateException("Cannot set category background if screen is using transparent background.");
             categoryBackground.put(categoryKey, identifier);
-        }, () -> dataMap.get(categoryKey));
+        }, () -> dataMap.get(categoryKey), () -> removeCategory(categoryKey));
     }
     
     @Override
     public ConfigBuilder removeCategory(String category) {
         if (dataMap.containsKey(category) && fallbackCategory.equals(category))
             fallbackCategory = null;
+        if (!dataMap.containsKey(category))
+            throw new NullPointerException("Category doesn't exist!");
         dataMap.remove(category);
         return this;
     }
@@ -184,34 +210,29 @@ public class ConfigBuilderImpl implements ConfigBuilder {
         return afterInitConsumer;
     }
     
+    @SuppressWarnings("deprecation")
     @Override
     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) {
             @Override
-            public void onSave(Map<String, List<Pair<String, Object>>> o) {
+            public void save() {
                 if (savingRunnable != null)
                     savingRunnable.run();
             }
             
-            @Override
-            public boolean isEditable() {
-                return editable;
-            }
-            
             @Override
             protected void init() {
                 super.init();
                 afterInitConsumer.accept(this);
             }
-            
-            @Override
-            public String getFallbackCategory() {
-                return fallbackCategory;
-            }
         };
+        screen.setEditable(editable);
+        screen.setFallbackCategory(fallbackCategory);
         screen.setSmoothScrollingTabs(tabsSmoothScroll);
+        screen.setTransparentBackground(transparentBackground);
+        screen.setAlwaysShowTabs(alwaysShowTabs);
         return screen;
     }
     

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

@@ -14,12 +14,14 @@ public class ConfigCategoryImpl implements ConfigCategory {
     
     private Supplier<List<Pair<String, Object>>> listSupplier;
     private Consumer<Identifier> backgroundConsumer;
+    private Runnable destroyCategory;
     private String categoryKey;
     
-    ConfigCategoryImpl(String categoryKey, Consumer<Identifier> backgroundConsumer, Supplier<List<Pair<String, Object>>> listSupplier) {
+    ConfigCategoryImpl(String categoryKey, Consumer<Identifier> backgroundConsumer, Supplier<List<Pair<String, Object>>> listSupplier, Runnable destroyCategory) {
         this.listSupplier = listSupplier;
         this.backgroundConsumer = backgroundConsumer;
         this.categoryKey = categoryKey;
+        this.destroyCategory = destroyCategory;
     }
     
     @Override
@@ -44,4 +46,9 @@ public class ConfigCategoryImpl implements ConfigCategory {
         return this;
     }
     
+    @Override
+    public void removeCategory() {
+        destroyCategory.run();
+    }
+    
 }

+ 61 - 0
src/main/java/me/shedaniel/clothconfig2/impl/ScissorsHandlerImpl.java

@@ -0,0 +1,61 @@
+package me.shedaniel.clothconfig2.impl;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.clothconfig2.api.ScissorsHandler;
+import me.shedaniel.math.api.Rectangle;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.util.Window;
+import org.lwjgl.opengl.GL11;
+
+import java.util.Collections;
+import java.util.List;
+
+public final class ScissorsHandlerImpl implements ScissorsHandler {
+    
+    @Deprecated public static final ScissorsHandler INSTANCE = new ScissorsHandlerImpl();
+    private List<Rectangle> scissorsAreas;
+    
+    public ScissorsHandlerImpl() {
+        this.scissorsAreas = Lists.newArrayList();
+    }
+    
+    @Override
+    public void clearScissors() {
+        scissorsAreas.clear();
+        applyScissors();
+    }
+    
+    @Override
+    public List<Rectangle> getScissorsAreas() {
+        return Collections.unmodifiableList(scissorsAreas);
+    }
+    
+    @Override
+    public void scissor(Rectangle rectangle) {
+        scissorsAreas.add(rectangle);
+        applyScissors();
+    }
+    
+    @Override
+    public void removeLastScissor() {
+        if (!scissorsAreas.isEmpty())
+            scissorsAreas.remove(scissorsAreas.size() - 1);
+        applyScissors();
+    }
+    
+    @Override
+    public void applyScissors() {
+        if (!scissorsAreas.isEmpty()) {
+            Rectangle r = scissorsAreas.get(0).clone();
+            scissorsAreas.stream().skip(1L).forEach(rectangle -> r.setBounds(r.intersects(rectangle) ? r.intersection(rectangle) : new Rectangle()));
+            Window window = MinecraftClient.getInstance().getWindow();
+            int x = Math.round(window.getWidth() * (r.x / (float) window.getScaledWidth()));
+            int y = Math.round(window.getHeight() * (r.y / (float) window.getScaledHeight()));
+            int ww = Math.round(window.getWidth() * (r.width / (float) window.getScaledWidth()));
+            int hh = Math.round(window.getHeight() * ((r.height) / (float) window.getScaledHeight()));
+            GL11.glEnable(GL11.GL_SCISSOR_TEST);
+            GL11.glScissor(x, window.getHeight() - hh - y, ww, hh);
+        } else
+            GL11.glDisable(GL11.GL_SCISSOR_TEST);
+    }
+}

+ 26 - 26
src/main/resources/assets/cloth-config2/lang/zh_cn.json

@@ -1,28 +1,28 @@
 {
-  "text.cloth-config.save_and_done": "Save Changes",
-  "text.cloth-config.quit_config": "Changes Not Saved",
-  "text.cloth-config.quit_config_sure": "Are you sure you want to quit editing the config? Changes will not be saved!",
-  "text.cloth-config.cancel_discard": "Cancel & Discard Changes",
-  "text.cloth-config.quit_discard": "Quit & Discard Changes",
-  "text.cloth-config.config": "Config",
-  "text.cloth-config.multi_error": "Multiple Issues!",
-  "text.cloth-config.not_editable": "Not Editable!",
-  "text.cloth-config.error.not_valid_number_int": "Not a valid number! (Integer)",
-  "text.cloth-config.error.not_valid_number_long": "Not a valid number! (Long)",
-  "text.cloth-config.error.not_valid_number_float": "Not a valid number! (Float)",
-  "text.cloth-config.error.not_valid_number_double": "Not a valid number! (Double)",
-  "text.cloth-config.error.too_large": "Too Large! (Maximum: %d)",
-  "text.cloth-config.error.too_small": "Too Small! (Minimum: %d)",
-  "text.cloth-config.list.add": "Insert New",
-  "text.cloth-config.list.remove": "Delete Selected",
-  "text.cloth-config.error_cannot_save": "Error!",
-  "text.cloth-config.reset_value": "Reset",
-  "text.cloth.reset_value": "Reset",
-  "text.cloth-config.restart_required": "Restart Required",
-  "text.cloth-config.restart_required_sub": "One of your modified settings requires Minecraft to be restarted. Do you want to proceed?",
-  "text.cloth-config.exit_minecraft": "Exit Minecraft",
-  "text.cloth-config.ignore_restart": "Ignore Restart",
-  "text.cloth-config.boolean.value.true": "§aYes",
-  "text.cloth-config.boolean.value.false": "§cNo",
-  "text.cloth-config.dropdown.value.unknown": "§cNo suggestions"
+  "text.cloth-config.save_and_done": "完成",
+  "text.cloth-config.quit_config": "更改尚未保存",
+  "text.cloth-config.quit_config_sure": "您确定要取消编辑吗?更改将不会被保存!",
+  "text.cloth-config.cancel_discard": "放弃变更",
+  "text.cloth-config.quit_discard": "放弃变更",
+  "text.cloth-config.config": "设置",
+  "text.cloth-config.multi_error": "多个问题!",
+  "text.cloth-config.not_editable": "不可编辑!",
+  "text.cloth-config.error.not_valid_number_int": "不是一个有效的数字! (整数)",
+  "text.cloth-config.error.not_valid_number_long": "不是一个有效的数字!(长整数)",
+  "text.cloth-config.error.not_valid_number_float": "不是一个有效的数字! (浮点数)",
+  "text.cloth-config.error.not_valid_number_double": "不是一个有效的数字! (Double)",
+  "text.cloth-config.error.too_large": "太大! (最大值: %d)",
+  "text.cloth-config.error.too_small": "太小! (最小值: %d)",
+  "text.cloth-config.list.add": "插入",
+  "text.cloth-config.list.remove": "删除所选",
+  "text.cloth-config.error_cannot_save": "错误!",
+  "text.cloth-config.reset_value": "重置",
+  "text.cloth.reset_value": "重置",
+  "text.cloth-config.restart_required": "需要重启",
+  "text.cloth-config.restart_required_sub": "您修改的设置要求Minecraft重新启动。您想要继续吗?",
+  "text.cloth-config.exit_minecraft": "退出我的世界",
+  "text.cloth-config.ignore_restart": "忽略重启",
+  "text.cloth-config.boolean.value.true": "§a",
+  "text.cloth-config.boolean.value.false": "§c",
+  "text.cloth-config.dropdown.value.unknown": "§c没有建议项"
 }

+ 9 - 9
src/main/resources/assets/cloth-config2/lang/zh_tw.json

@@ -1,16 +1,16 @@
 {
   "text.cloth-config.save_and_done": "完成",
   "text.cloth-config.quit_config": "更改尚未保存",
-  "text.cloth-config.quit_config_sure": "Are you sure you want to quit editing the config? Changes will not be saved!",
-  "text.cloth-config.cancel_discard": "Cancel & Discard Changes",
+  "text.cloth-config.quit_config_sure": "您確定要取消編輯嗎?更改將不會被保存!",
+  "text.cloth-config.cancel_discard": "放棄變更",
   "text.cloth-config.quit_discard": "放棄變更",
   "text.cloth-config.config": "設置",
-  "text.cloth-config.multi_error": "多重錯誤!",
+  "text.cloth-config.multi_error": "多個問題!",
   "text.cloth-config.not_editable": "不可編輯!",
-  "text.cloth-config.error.not_valid_number_int": "不是有效的數字!(Integer)",
-  "text.cloth-config.error.not_valid_number_long": "不是有效的數字!(Long)",
-  "text.cloth-config.error.not_valid_number_float": "不是有效的數字!(Float)",
-  "text.cloth-config.error.not_valid_number_double": "不是有效的數字!(Double)",
+  "text.cloth-config.error.not_valid_number_int": "不是一個有效的數字! (整數)",
+  "text.cloth-config.error.not_valid_number_long": "不是一個有效的數字! (長整數)",
+  "text.cloth-config.error.not_valid_number_float": "不是一個有效的數字! (浮點數)",
+  "text.cloth-config.error.not_valid_number_double": "不是一個有效的數字! (Double)",
   "text.cloth-config.error.too_large": "太大!(最大值:%d)",
   "text.cloth-config.error.too_small": "太小!(最小值:%d)",
   "text.cloth-config.list.add": "插入",
@@ -19,10 +19,10 @@
   "text.cloth-config.reset_value": "重置",
   "text.cloth.reset_value": "重置",
   "text.cloth-config.restart_required": "需要重新啟動",
-  "text.cloth-config.restart_required_sub": "One of your modified settings requires Minecraft to be restarted. Do you want to proceed?",
+  "text.cloth-config.restart_required_sub": "您修改的設置要求Minecraft重新啟動。您要重啟嗎?",
   "text.cloth-config.exit_minecraft": "結束 Minecraft",
   "text.cloth-config.ignore_restart": "忽略重啟",
   "text.cloth-config.boolean.value.true": "§a是",
   "text.cloth-config.boolean.value.false": "§c否",
-  "text.cloth-config.dropdown.value.unknown": "§cNo suggestions"
+  "text.cloth-config.dropdown.value.unknown": "§c沒有建議項"
 }