diff options
4 files changed, 137 insertions, 20 deletions
diff --git a/build.gradle b/build.gradle index 0bf075b..1bd45f8 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ dependencies { compileOnly ("com.google.code.findbugs:jsr305:3.0.2") { transitive = false } - def modmenu = "1.8.1+build.17" + def modmenu = "1.8.5+build.23" modCompileOnly "io.github.prospector:modmenu:$modmenu" modRuntime "io.github.prospector:modmenu:$modmenu" // for testing } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java index 65cf95f..24cf6ab 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java @@ -5,6 +5,8 @@ import io.github.cottonmc.cotton.gui.widget.WWidget; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; +import javax.annotation.Nullable; + /** * Background painters are used to paint the background of a widget. * The background painter instance of a widget can be changed to customize the look of a widget. @@ -128,11 +130,30 @@ public interface BackgroundPainter { * * <p>Nine-patch textures are separated into nine sections: four corners, four edges and a center part. * The edges and the center are either tiled or stretched, depending on the {@linkplain BackgroundPainter.NinePatch.Mode mode}, - * to fill the area between the corners. The default mode is {@link BackgroundPainter.NinePatch.Mode#TILING}. + * to fill the area between the corners. By default, the texture mode is loaded from the texture metadata. + * The default mode for that is {@link BackgroundPainter.NinePatch.Mode#STRETCHING}. * * <p>{@code NinePatch} painters have a customizable padding that can be applied. * For example, a GUI panel for a container block might have a padding of 8 pixels, like {@link BackgroundPainter#VANILLA}. * You can set the padding using {@link NinePatch#setPadding(int)}. + * + * <h2>Nine-patch metadata</h2> + * You can specify metadata for a nine-patch texture in a resource pack by creating a metadata file. + * Metadata files can currently specify the filling mode of the painter that paints the texture. + * <p>The metadata file for a texture has to be placed in the same directory as the texture. + * The file name must be {@code X.9patch} where X is the texture file name (including .png). + * <p>Metadata files use {@linkplain java.util.Properties .properties format} with the following keys: + * <table border="1"> + * <caption>Properties</caption> + * <tr> + * <th>Key</th> + * <th>Value</th> + * </tr> + * <tr> + * <td>{@code mode}</td> + * <td>{@link Mode#STRETCHING stretching} | {@link Mode#TILING tiling}</td> + * </tr> + * </table> */ public static class NinePatch implements BackgroundPainter { private final Identifier texture; @@ -142,7 +163,7 @@ public interface BackgroundPainter { private int leftPadding = 0; private int bottomPadding = 0; private int rightPadding = 0; - private Mode mode = Mode.STRETCHING; + private Mode mode = null; /** * Creates a nine-patch background painter with 4 px corners and a 0.25 cornerUv (corner fraction of whole texture). @@ -234,28 +255,18 @@ public interface BackgroundPainter { return cornerUv; } + @Nullable public Mode getMode() { return mode; } - public NinePatch setMode(Mode mode) { - this.mode = mode; - return this; - } - - /** - * Sets the {@linkplain Mode mode} of this painter to {@link Mode#STRETCHING}. - */ - public NinePatch stretch() { - this.mode = Mode.STRETCHING; - return this; - } - /** - * Sets the {@linkplain Mode mode} of this painter to {@link Mode#TILING}. + * Sets the {@linkplain Mode mode} of this painter to the specified mode. + * <p>If the {@code mode} is not null, it will override the one specified in the texture metadata. + * A null mode uses the texture metadata. */ - public NinePatch tile() { - this.mode = Mode.TILING; + public NinePatch setMode(@Nullable Mode mode) { + this.mode = mode; return this; } @@ -271,6 +282,7 @@ public interface BackgroundPainter { int y2 = top + height - cornerSize; float uv1 = cornerUv; float uv2 = 1.0f - cornerUv; + Mode mode = this.mode != null ? this.mode : NinePatchMetadataLoader.INSTANCE.getProperties(texture).getMode(); ScreenDrawing.texturedRect(left, top, cornerSize, cornerSize, texture, 0, 0, uv1, uv1, 0xFF_FFFFFF); ScreenDrawing.texturedRect(x2, top, cornerSize, cornerSize, texture, uv2, 0, 1, uv1, 0xFF_FFFFFF); @@ -322,14 +334,24 @@ public interface BackgroundPainter { public enum Mode { /** * The texture is stretched to fill the edges and the center. + * This is the default mode. */ STRETCHING, /** * The texture is tiled to fill the edges and the center. - * This is the default mode. */ TILING; + + @Nullable + static Mode fromString(String str) { + if (str == null) return null; + + if (str.equalsIgnoreCase("stretching")) return STRETCHING; + if (str.equalsIgnoreCase("tiling")) return TILING; + + return null; + } } } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/LibGuiClient.java b/src/main/java/io/github/cottonmc/cotton/gui/client/LibGuiClient.java index b06369a..d457e50 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/LibGuiClient.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/LibGuiClient.java @@ -5,8 +5,10 @@ import blue.endless.jankson.JsonElement; import blue.endless.jankson.JsonObject; import io.github.cottonmc.jankson.JanksonFactory; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.resource.ResourceType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -24,6 +26,8 @@ public class LibGuiClient implements ClientModInitializer { @Override public void onInitializeClient() { config = loadConfig(); + + ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(NinePatchMetadataLoader.INSTANCE); } public static LibGuiConfig loadConfig() { diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatchMetadataLoader.java b/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatchMetadataLoader.java new file mode 100644 index 0000000..daa1b45 --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatchMetadataLoader.java @@ -0,0 +1,91 @@ +package io.github.cottonmc.cotton.gui.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.SinglePreparationResourceReloadListener; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import java.io.InputStream; +import java.util.*; + +@Environment(EnvType.CLIENT) +public class NinePatchMetadataLoader extends SinglePreparationResourceReloadListener<Map<Identifier, Properties>> implements IdentifiableResourceReloadListener { + public static final NinePatchMetadataLoader INSTANCE = new NinePatchMetadataLoader(); + + private static final Identifier ID = new Identifier("libgui", "9patch_metadata"); + private static final String SUFFIX = ".9patch"; + + private Map<Identifier, TextureProperties> properties = Collections.emptyMap(); + + public TextureProperties getProperties(Identifier texture) { + return properties.getOrDefault(texture, TextureProperties.DEFAULT); + } + + @Override + public Identifier getFabricId() { + return ID; + } + + @Override + protected Map<Identifier, Properties> prepare(ResourceManager manager, Profiler profiler) { + Collection<Identifier> ids = manager.findResources("textures", s -> s.endsWith(SUFFIX)); + Map<Identifier, Properties> result = new HashMap<>(); + + for (Identifier input : ids) { + try (Resource resource = manager.getResource(input); + InputStream stream = resource.getInputStream()) { + Properties props = new Properties(); + props.load(stream); + Identifier textureId = new Identifier(input.getNamespace(), input.getPath().substring(0, input.getPath().length() - SUFFIX.length())); + result.put(textureId, props); + } catch (Exception e) { + LibGuiClient.logger.error("Error while loading metadata file {}, skipping...", input, e); + } + } + + return result; + } + + @Override + protected void apply(Map<Identifier, Properties> meta, ResourceManager manager, Profiler profiler) { + properties = new HashMap<>(); + for (Map.Entry<Identifier, Properties> entry : meta.entrySet()) { + Identifier id = entry.getKey(); + Properties props = entry.getValue(); + + if (!props.containsKey("mode")) { + LibGuiClient.logger.error("Metadata file for nine-patch texture {} is missing required key 'mode'", id); + continue; + } + + String modeStr = props.getProperty("mode"); + BackgroundPainter.NinePatch.Mode mode = BackgroundPainter.NinePatch.Mode.fromString(modeStr); + + if (mode == null) { + LibGuiClient.logger.error("Invalid mode '{}' in nine-patch metadata file for texture {}", modeStr, id); + continue; + } + + TextureProperties texProperties = new TextureProperties(mode); + properties.put(id, texProperties); + } + } + + public static class TextureProperties { + public static final TextureProperties DEFAULT = new TextureProperties(BackgroundPainter.NinePatch.Mode.STRETCHING); + + private final BackgroundPainter.NinePatch.Mode mode; + + public TextureProperties(BackgroundPainter.NinePatch.Mode mode) { + this.mode = mode; + } + + public BackgroundPainter.NinePatch.Mode getMode() { + return mode; + } + } +} |