diff --git a/build-src/src/main/kotlin/moulconfig.base.gradle.kts b/build-src/src/main/kotlin/moulconfig.base.gradle.kts index 1b6e46119..75d6785ee 100644 --- a/build-src/src/main/kotlin/moulconfig.base.gradle.kts +++ b/build-src/src/main/kotlin/moulconfig.base.gradle.kts @@ -1,5 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.external.javadoc.StandardJavadocDocletOptions import java.nio.charset.StandardCharsets +import java.util.zip.ZipFile repositories { mavenLocal() @@ -17,9 +19,64 @@ tasks.withType(JavaCompile::class) { options.encoding = StandardCharsets.UTF_8.name() } +// TODO: fix warnings instead of suppressing them +tasks.withType(Javadoc::class).configureEach { + (options as StandardJavadocDocletOptions).addBooleanOption("Xdoclint:all,-missing", true) +} + tasks.withType(ShadowJar::class).configureEach { relocate("juuxel.libninepatch", "io.github.notenoughupdates.moulconfig.deps.libninepatch") } + +val checkJarForKotlinRuntime by tasks.registering { + group = "verification" + description = "Fails if built jars contain Kotlin runtime classes or Kotlin class references." + val jarTasks = tasks.withType(Jar::class) + dependsOn(jarTasks) + doLast { + fun ByteArray.containsBytes(needle: ByteArray): Boolean { + if (needle.isEmpty() || needle.size > size) return false + for (i in 0..(size - needle.size)) { + var matches = true + for (j in needle.indices) { + if (this[i + j] != needle[j]) { + matches = false + break + } + } + if (matches) return true + } + return false + } + jarTasks.forEach { jarTask -> + val jar = jarTask.archiveFile.get().asFile + if (!jar.exists() || jarTask.archiveClassifier.orNull == "sources") return@forEach + ZipFile(jar).use { zip -> + val badEntries = zip.entries().asSequence() + .map { it.name } + .filter { it.startsWith("kotlin/") || it.startsWith("kotlinx/") || it.endsWith(".kotlin_module") } + .toList() + if (badEntries.isNotEmpty()) { + error("Kotlin runtime content found in ${jar.name}: ${badEntries.take(10)}") + } + val badClass = zip.entries().asSequence() + .filter { !it.isDirectory && it.name.endsWith(".class") } + .firstOrNull { entry -> + val bytes = zip.getInputStream(entry).readBytes() + bytes.containsBytes("kotlin/".toByteArray()) || bytes.containsBytes("kotlin.".toByteArray()) || + bytes.containsBytes("kotlinx/".toByteArray()) || bytes.containsBytes("kotlinx.".toByteArray()) + } + if (badClass != null) { + error("Kotlin class reference found in ${jar.name}: ${badClass.name}") + } + } + } + } +} + +tasks.matching { it.name == "check" }.configureEach { + dependsOn(checkJarForKotlinRuntime) +} afterEvaluate { extensions.findByType()?.apply { repositories { diff --git a/build-src/src/main/kotlin/moulconfig.fabric.gradle.kts b/build-src/src/main/kotlin/moulconfig.fabric.gradle.kts index 7c57af41a..f12be9fae 100644 --- a/build-src/src/main/kotlin/moulconfig.fabric.gradle.kts +++ b/build-src/src/main/kotlin/moulconfig.fabric.gradle.kts @@ -6,7 +6,6 @@ import xyz.wagyourtail.unimined.api.minecraft.task.RemapJarTask plugins { id("xyz.wagyourtail.unimined") - id("moulconfig.kotlin") id("moulconfig.leaf") id("moulconfig.manifold") } @@ -132,6 +131,11 @@ sourceSets.main { } } +tasks.named("javadoc") { + dependsOn(tasks.compileJava) + setSource(preProcessorArgs.preprocessedSources) +} + tasks.withType(Jar::class) { this.filesMatching(listOf("fabric.mod.json")) { filter { @@ -179,4 +183,4 @@ modernProj.configure { } } } -} \ No newline at end of file +} diff --git a/build-src/src/main/kotlin/moulconfig.leaf.gradle.kts b/build-src/src/main/kotlin/moulconfig.leaf.gradle.kts index bb1d32018..7a197235b 100644 --- a/build-src/src/main/kotlin/moulconfig.leaf.gradle.kts +++ b/build-src/src/main/kotlin/moulconfig.leaf.gradle.kts @@ -1,8 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import xyz.wagyourtail.unimined.util.sourceSets plugins { + java `maven-publish` idea id("moulconfig.base") @@ -11,6 +10,7 @@ plugins { } val shadowInclude by configurations.creating +pluginManager.apply("java") dependencies { "implementation"(project(":common")) shadowInclude(project(":common", configuration = "singleFile")) @@ -18,6 +18,8 @@ dependencies { shadowInclude(Dependencies.LIB_NINE_PATCH) compileOnly(Dependencies.JB_ANNOTATIONS) compileOnly(Dependencies.JSPECIFY) + "annotationProcessor"(Dependencies.LOMBOK) + compileOnly(Dependencies.LOMBOK) } val shadowJar by tasks.named("shadowJar", ShadowJar::class) { @@ -29,13 +31,10 @@ val processResources = tasks.named("processResources", Copy::class) { } val sourcesJar by tasks.creating(Jar::class) { - from(sourceSets.named("main").map { it.allSource }) - from(project(":common").the().getByName("main").allSource) + from(file("src/main/java")) + from(project(":common").file("src/main/java")) archiveClassifier.set("sources") } -tasks.withType { - compilerOptions.freeCompilerArgs.add("-Xmetadata-version=2.0.0") -} configure { publications { defaultMaven { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 18a100ebd..829dc1b15 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,6 +1,5 @@ plugins { java - id("moulconfig.kotlin") id("moulconfig.dokka.base") `maven-publish` id("moulconfig.base") @@ -51,5 +50,3 @@ dokka { } } } - - diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.java new file mode 100644 index 000000000..da3b0d08b --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.java @@ -0,0 +1,301 @@ +package io.github.notenoughupdates.moulconfig; + +import com.google.gson.annotations.Expose; + +import java.awt.Color; +import java.util.Objects; + +@SuppressWarnings({"DeprecatedIsStillUsed", "deprecation"}) +public final class ChromaColour { + @Expose + private final float hue; + @Expose + private final float saturation; + @Expose + private final float brightness; + @Expose + private final int timeForFullRotationInMillis; + @Expose + private final int alpha; + + /** + * The value of {@link #evaluateColourWithShift(double)} at {@link #cachedRGBHueOffset}. + */ + private transient int cachedRGB; + + /** + * The last queried value of {@link #evaluateColourWithShift(double)}. + */ + private transient double cachedRGBHueOffset = Double.NaN; + + public ChromaColour(float hue, float saturation, float brightness, int timeForFullRotationInMillis, int alpha) { + this.hue = hue; + this.saturation = saturation; + this.brightness = brightness; + this.timeForFullRotationInMillis = timeForFullRotationInMillis; + this.alpha = alpha; + } + + /** + * Hue in a range from 0 to 1. For a chroma colour this is added to the time as an offset. + */ + public float getHue() { + return hue; + } + + /** + * Saturation in a range from 0 to 1. + */ + public float getSaturation() { + return saturation; + } + + /** + * Brightness in a range from 0 to 1. + */ + public float getBrightness() { + return brightness; + } + + /** + * If set to 0, this indicates a static colour. If set to a value above 0, indicates the amount of milliseconds that pass until the same colour is met again. + * This value may be saved lossy. + */ + public int getTimeForFullRotationInMillis() { + return timeForFullRotationInMillis; + } + + /** + * Alpha in a range from 0 to 255 (with 255 being fully opaque). + */ + public int getAlpha() { + return alpha; + } + + private int evaluateColourWithShift(double hueShift) { + if (Math.abs(cachedRGBHueOffset - hueShift) < 1 / 360.0) { + return cachedRGB; + } + float effectiveHue = (float) ((hue + hueShift) % 1); + int ret = (Color.HSBtoRGB(effectiveHue, saturation, brightness) & 0x00FFFFFF) | (alpha << 24); + cachedRGBHueOffset = hueShift; + cachedRGB = ret; + return ret; + } + + /** + * @param offset offset the colour by a hue amount. + * @return the colour, at the current time if this is a chrome colour + */ + public int getEffectiveColourRGB(float offset) { + double effectiveHueOffset = timeForFullRotationInMillis > 0 + ? System.currentTimeMillis() / (double) timeForFullRotationInMillis + : 0.0; + effectiveHueOffset += offset; + return evaluateColourWithShift(effectiveHueOffset); + } + + /** + * @param offset offset the colour by a hue amount. + * @return the colour, at the current time if this is a chrome colour + */ + public Color getEffectiveColour(float offset) { + return new Color(getEffectiveColourRGB(offset), true); + } + + /** + * Unlike {@link #getEffectiveColourRGB(float)}, this offset does not change anything if not using an animated colour. + * + * @param offset offset the colour by a time amount in milliseconds. + * @return the colour, at the current time if this is a chrome colour + */ + public int getEffectiveColourWithTimeOffsetRGB(int offset) { + if (timeForFullRotationInMillis == 0) { + return evaluateColourWithShift(0.0); + } + double effectiveHue = (System.currentTimeMillis() + offset) / (double) timeForFullRotationInMillis; + return evaluateColourWithShift(effectiveHue); + } + + /** + * Unlike {@link #getEffectiveColour(float)}, this offset does not change anything if not using an animated colour. + * + * @param offset offset the colour by a time amount in milliseconds. + * @return the colour, at the current time if this is a chrome colour + */ + public Color getEffectiveColourWithTimeOffset(int offset) { + return new Color(getEffectiveColourWithTimeOffsetRGB(offset), true); + } + + /** + * @return the colour, at the current time if this is a chrome colour + */ + public int getEffectiveColourRGB() { + return getEffectiveColourWithTimeOffsetRGB(0); + } + + /** + * @return the colour, at the current time if this is a chrome colour + */ + public Color getEffectiveColour() { + return getEffectiveColourWithTimeOffset(0); + } + + @Deprecated + public String toLegacyString() { + int namedSpeed = timeForFullRotationInMillis == 0 ? 0 : getSpeedForMillis(timeForFullRotationInMillis / 1000F); + int rgb = evaluateColourWithShift(0.0); + int red = rgb >> 16 & 0xFF; + int green = rgb >> 8 & 0xFF; + int blue = rgb & 0xFF; + return special(namedSpeed, alpha, red, green, blue); + } + + @Deprecated + public static String special(int chromaSpeed, int alpha, int rgb) { + return special(chromaSpeed, alpha, rgb >> 16 & 0xFF, rgb >> 8 & 0xFF, rgb & 0xFF); + } + + private static final int RADIX = 10; + + @Deprecated + public static String special(int chromaSpeed, int alpha, int r, int g, int b) { + StringBuilder sb = new StringBuilder(); + sb.append(Integer.toString(chromaSpeed, RADIX)).append(':'); + sb.append(Integer.toString(alpha, RADIX)).append(':'); + sb.append(Integer.toString(r, RADIX)).append(':'); + sb.append(Integer.toString(g, RADIX)).append(':'); + sb.append(Integer.toString(b, RADIX)); + return sb.toString(); + } + + private static int[] decompose(String csv) { + String[] split = csv.split(":"); + int[] arr = new int[split.length]; + for (int i = 0; i < split.length; i++) { + try { + arr[i] = Integer.parseInt(split[split.length - 1 - i], RADIX); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + return arr; + } + + @Deprecated + public static int specialToSimpleRGB(String special) { + int[] d = decompose(special); + int b = d[0]; + int g = d[1]; + int r = d[2]; + int a = d[3]; + return (a & 0xFF) << 24 | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + } + + @Deprecated + public static int getSpeed(String special) { + return decompose(special)[4]; + } + + private static final int MIN_CHROMA_SECS = 1; + private static final int MAX_CHROMA_SECS = 60; + + @Deprecated + public static float getSecondsForSpeed(int speed) { + return (255 - speed) / 254F * (MAX_CHROMA_SECS - MIN_CHROMA_SECS) + MIN_CHROMA_SECS; + } + + @Deprecated + public static int getSpeedForMillis(float seconds) { + return Math.round(255 - ((seconds - MIN_CHROMA_SECS) / (MAX_CHROMA_SECS - MIN_CHROMA_SECS) * 254)); + } + + @Deprecated + public static int specialToChromaRGB(String special) { + int[] d = decompose(special); + int b = d[0]; + int g = d[1]; + int r = d[2]; + int a = d[3]; + int chr = d[4]; + float[] hsv = Color.RGBtoHSB(r, g, b, null); + if (chr > 0) { + float seconds = getSecondsForSpeed(chr); + hsv[0] += (float) ((System.currentTimeMillis() / 1000.0 / seconds) % 1); + hsv[0] %= 1F; + if (hsv[0] < 0) { + hsv[0] += 1F; + } + } + return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF); + } + + @Deprecated + public static int rotateHue(int argb, int degrees) { + int a = argb >> 24 & 0xFF; + int r = argb >> 16 & 0xFF; + int g = argb >> 8 & 0xFF; + int b = argb & 0xFF; + float[] hsv = Color.RGBtoHSB(r, g, b, null); + hsv[0] += degrees / 360F; + hsv[0] %= 1F; + return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF); + } + + @Deprecated + public static ChromaColour forLegacyString(String stringRepresentation) { + int[] d = decompose(stringRepresentation); + assert d.length == 5; + int chr = d[4]; + int a = d[3]; + int r = d[2]; + int g = d[1]; + int b = d[0]; + return fromRGB(r, g, b, chr > 0 ? (int) (getSecondsForSpeed(chr) * 1000) : 0, a); + } + + public static ChromaColour fromStaticRGB(int r, int g, int b, int a) { + return fromRGB(r, g, b, 0, a); + } + + public static ChromaColour fromRGB(int r, int g, int b, int chromaSpeedMillis, int a) { + float[] floats = Color.RGBtoHSB(r, g, b, null); + return new ChromaColour(floats[0], floats[1], floats[2], chromaSpeedMillis, a); + } + + public ChromaColour copy(float hue, float saturation, float brightness, int timeForFullRotationInMillis, int alpha) { + return new ChromaColour(hue, saturation, brightness, timeForFullRotationInMillis, alpha); + } + + public float component1() { return hue; } + public float component2() { return saturation; } + public float component3() { return brightness; } + public int component4() { return timeForFullRotationInMillis; } + public int component5() { return alpha; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ChromaColour)) return false; + ChromaColour that = (ChromaColour) o; + return Float.compare(that.hue, hue) == 0 + && Float.compare(that.saturation, saturation) == 0 + && Float.compare(that.brightness, brightness) == 0 + && timeForFullRotationInMillis == that.timeForFullRotationInMillis + && alpha == that.alpha; + } + + @Override + public int hashCode() { + return Objects.hash(hue, saturation, brightness, timeForFullRotationInMillis, alpha); + } + + @Override + public String toString() { + return "ChromaColour(hue=" + hue + + ", saturation=" + saturation + + ", brightness=" + brightness + + ", timeForFullRotationInMillis=" + timeForFullRotationInMillis + + ", alpha=" + alpha + ")"; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.kt deleted file mode 100644 index a757943dd..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/ChromaColour.kt +++ /dev/null @@ -1,238 +0,0 @@ -package io.github.notenoughupdates.moulconfig - -import com.google.gson.annotations.Expose -import java.awt.Color -import kotlin.math.abs -import kotlin.math.roundToInt - -@Suppress("DeprecatedCallableAddReplaceWith", "DEPRECATION") -data class ChromaColour( - /** - * Hue in a range from 0 to 1. For a chroma colour this is added to the time as an offset. - */ - @Expose - val hue: Float, - /** - * Saturation in a range from 0 to 1 - */ - @Expose - val saturation: Float, - /** - * Brightness in a range from 0 to 1 - */ - @Expose - val brightness: Float, - /** - * If set to 0, this indicates a static colour. If set to a value above 0, indicates the amount of milliseconds that pass until the same colour is met again. - * This value may be saved lossy. - */ - @Expose - val timeForFullRotationInMillis: Int, - /** - * Alpha in a range from 0 to 255 (with 255 being fully opaque). - */ - @Expose - val alpha: Int, -) { - - private fun evaluateColourWithShift(hueShift: Double): Int { - if (abs(cachedRGBHueOffset - hueShift) < 1 / 360.0) return cachedRGB - val effectiveHue = ((hue.toDouble() + hueShift) % 1).toFloat() - val ret = (Color.HSBtoRGB(effectiveHue, saturation, brightness) and 0x00FFFFFF) or (alpha shl 24) - cachedRGBHueOffset = hueShift - cachedRGB = ret - return ret - } - - /** - * The value of [evaluateColourWithShift] at [cachedRGBHueOffset] - */ - @Transient - private var cachedRGB: Int = 0 - - /** - * The last queried value of [evaluateColourWithShift]. - */ - @Transient - private var cachedRGBHueOffset: Double = Double.NaN - - /** - * @param offset offset the colour by a hue amount. - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColourRGB(offset: Float): Int { - var effectiveHueOffset = if (timeForFullRotationInMillis > 0) { - System.currentTimeMillis() / timeForFullRotationInMillis.toDouble() - } else { - .0 - } - effectiveHueOffset += offset - return evaluateColourWithShift(effectiveHueOffset) - } - - /** - * @param offset offset the colour by a hue amount. - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColour(offset: Float): Color = Color(getEffectiveColourRGB(offset), true) - - /** - * Unlike [getEffectiveColourRGB], this offset does not change anything if not using an animated colour. - * - * @param offset offset the colour by a time amount in milliseconds. - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColourWithTimeOffsetRGB(offset: Int): Int { - if (timeForFullRotationInMillis == 0) return evaluateColourWithShift(.0) - val effectiveHue = (System.currentTimeMillis() + offset) / timeForFullRotationInMillis.toDouble() - return evaluateColourWithShift(effectiveHue) - } - - /** - * Unlike [getEffectiveColour], this offset does not change anything if not using an animated colour. - * - * @param offset offset the colour by a time amount in milliseconds. - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColourWithTimeOffset(offset: Int): Color = Color(getEffectiveColourWithTimeOffsetRGB(offset), true) - - /** - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColourRGB(): Int = getEffectiveColourWithTimeOffsetRGB(0) - - /** - * @return the colour, at the current time if this is a chrome colour - */ - fun getEffectiveColour(): Color = getEffectiveColourWithTimeOffset(0) - - @Deprecated("") - fun toLegacyString(): String { - val namedSpeed = - if (timeForFullRotationInMillis == 0) 0 else getSpeedForMillis(timeForFullRotationInMillis / 1000f) - val rgb = evaluateColourWithShift(.0) - val red = rgb shr 16 and 0xFF - val green = rgb shr 8 and 0xFF - val blue = rgb and 0xFF - return special(namedSpeed, alpha, red, green, blue) - } - - companion object { - - @JvmStatic - @Deprecated("") - fun special(chromaSpeed: Int, alpha: Int, rgb: Int): String { - return special(chromaSpeed, alpha, rgb shr 16 and 0xFF, rgb shr 8 and 0xFF, rgb and 0xFF) - } - - private const val RADIX: Int = 10 - - @JvmStatic - @Deprecated("") - fun special(chromaSpeed: Int, alpha: Int, r: Int, g: Int, b: Int): String { - val sb = StringBuilder() - sb.append(chromaSpeed.toString(RADIX)).append(":") - sb.append(alpha.toString(RADIX)).append(":") - sb.append(r.toString(RADIX)).append(":") - sb.append(g.toString(RADIX)).append(":") - sb.append(b.toString(RADIX)) - return sb.toString() - } - - @JvmStatic - private fun decompose(csv: String): IntArray { - val split = csv.split(":") - - val arr = IntArray(split.size) - - for (i in split.indices) { - try { - arr[i] = split[split.size - 1 - i].toInt(RADIX) - } catch (e: NumberFormatException) { - e.printStackTrace() - } - } - return arr - } - - @JvmStatic - @Deprecated("") - fun specialToSimpleRGB(special: String): Int { - val (b, g, r, a) = decompose(special) - - return (a and 0xFF) shl 24 or ((r and 0xFF) shl 16) or ((g and 0xFF) shl 8) or (b and 0xFF) - } - - @JvmStatic - @Deprecated("") - fun getSpeed(special: String): Int = decompose(special)[4] - - private const val MIN_CHROMA_SECS: Int = 1 - private const val MAX_CHROMA_SECS: Int = 60 - - @JvmStatic - @Deprecated("") - fun getSecondsForSpeed(speed: Int): Float = (255 - speed) / 254f * (MAX_CHROMA_SECS - MIN_CHROMA_SECS) + MIN_CHROMA_SECS - - @Deprecated("") - fun getSpeedForMillis(seconds: Float): Int { - return (255 - ((seconds - MIN_CHROMA_SECS) / (MAX_CHROMA_SECS - MIN_CHROMA_SECS) * 254)).roundToInt() - } - - @JvmStatic - @Deprecated("") - fun specialToChromaRGB(special: String): Int { - val (b, g, r, a, chr) = decompose(special) - - val hsv = Color.RGBtoHSB(r, g, b, null) - - if (chr > 0) { - val seconds = getSecondsForSpeed(chr) - hsv[0] += (((System.currentTimeMillis().toDouble()) / 1000.0 / seconds) % 1).toFloat() - hsv[0] %= 1f - if (hsv[0] < 0) hsv[0] += 1f - } - - return (a and 0xFF) shl 24 or (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) and 0x00FFFFFF) - } - - @JvmStatic - @Deprecated("") - fun rotateHue(argb: Int, degrees: Int): Int { - val a = (argb shr 24) and 0xFF - val r = (argb shr 16) and 0xFF - val g = (argb shr 8) and 0xFF - val b = (argb) and 0xFF - - val hsv = Color.RGBtoHSB(r, g, b, null) - - hsv[0] += degrees / 360f - hsv[0] %= 1f - - return (a and 0xFF) shl 24 or (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) and 0x00FFFFFF) - } - - @JvmStatic - @Deprecated("") - fun forLegacyString(stringRepresentation: String): ChromaColour { - val d = decompose(stringRepresentation) - assert(d.size == 5) - - val chr = d[4] - val a = d[3] - val r = d[2] - val g = d[1] - val b = d[0] - return fromRGB(r, g, b, if (chr > 0) (getSecondsForSpeed(chr) * 1000).toInt() else 0, a) - } - - @JvmStatic - fun fromStaticRGB(r: Int, g: Int, b: Int, a: Int): ChromaColour = fromRGB(r, g, b, 0, a) - - @JvmStatic - fun fromRGB(r: Int, g: Int, b: Int, chromaSpeedMillis: Int, a: Int): ChromaColour { - val floats = Color.RGBtoHSB(r, g, b, null) - return ChromaColour(floats[0], floats[1], floats[2], chromaSpeedMillis, a) - } - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorColour.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorColour.java index 12c39498b..6c449f1b1 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorColour.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorColour.java @@ -32,7 +32,7 @@ * Used for colours. The field associated with this option may only be of type {@link String}. *

* To interpret the String value see {@link ChromaColour#forLegacyString}. You can use a - * {@link Property Property} and {@link Property#map} for more ergonomic use. + * {@link Property Property<String>} and {@link Property#map} for more ergonomic use. * To create a default value use {@link ChromaColour#special(int, int, int, int, int)}. *

* In the future, {@link ChromaColour} may also be used as a field type. diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorDraggableList.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorDraggableList.java index 5af617795..016eddd7d 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorDraggableList.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/ConfigEditorDraggableList.java @@ -27,7 +27,7 @@ import java.util.List; /** - * Used for draggable lists. The field associated with this option may only be of type {@link List List}. + * Used for draggable lists. The field associated with this option may only be of type {@link List List<T>}. * That {@code T} may only be either an {@code int}, or an enum. If an {@code int} is used, {@link #exampleText()} * needs to be provided, otherwise it needs to be kept empty. *

diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.java new file mode 100644 index 000000000..3dcc45098 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.java @@ -0,0 +1,25 @@ +package io.github.notenoughupdates.moulconfig.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Add additional search tags to your {@link ConfigOption}. These search tags will not appear anywhere user facing, but + * the {@link io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor#fulfillsSearch} function will use them to filter + * additional elements in the search. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Repeatable(SearchTag.Container.class) +public @interface SearchTag { + String value(); + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface Container { + SearchTag[] value(); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.kt deleted file mode 100644 index fcd841937..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/annotations/SearchTag.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.notenoughupdates.moulconfig.annotations - -/** - * Add additional search tags to your [ConfigOption]. These search tags will not appear anywhere user facing, but - * the [io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor.fulfillsSearch] function will use them to filter - * additional elements in the search - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.FIELD) -@Repeatable -annotation class SearchTag( - val value: String -) \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.java new file mode 100644 index 000000000..9e132af76 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.java @@ -0,0 +1,6 @@ +package io.github.notenoughupdates.moulconfig.common; + +public enum ClickType { + OPEN_LINK, + RUN_COMMAND +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.kt deleted file mode 100644 index 12a51cbec..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/ClickType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -enum class ClickType { - OPEN_LINK, RUN_COMMAND -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.java new file mode 100644 index 000000000..beb28b451 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.java @@ -0,0 +1,49 @@ +package io.github.notenoughupdates.moulconfig.common; + +import org.jetbrains.annotations.ApiStatus; + +import java.awt.image.BufferedImage; +import java.io.Closeable; + +/** + * A dynamically loaded texture. Must be destroyed manually. + */ +@ApiStatus.NonExtendable +public abstract class DynamicTextureReference implements Closeable { + private boolean wasDestroyed; + + /** + * An opaque reference to this dynamic texture. Can be used with {@link RenderContext#drawTexturedRect}. + */ + public abstract MyResourceLocation getIdentifier(); + + /** + * Destroy this texture. Using {@link #getIdentifier()} after calling this will cause issues. + */ + public final void destroy() { + if (!wasDestroyed) { + doDestroy(); + } + wasDestroyed = true; + } + + public abstract void update(BufferedImage bufferedImage); + + protected abstract void doDestroy(); + + @Override + public final void close() { + destroy(); + } + + @Override + protected void finalize() throws Throwable { + try { + if (!wasDestroyed) { + IMinecraft.INSTANCE.getLogger("DynamicTextureReference").warn("Dangling DynamicTextureReference"); + } + } finally { + super.finalize(); + } + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.kt deleted file mode 100644 index 8777dd718..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/DynamicTextureReference.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -import org.jetbrains.annotations.ApiStatus -import java.awt.image.BufferedImage -import java.io.Closeable - -/** - * A dynamically loaded texture. Must be destroyed manually. - */ -@ApiStatus.NonExtendable -abstract class DynamicTextureReference : Closeable { - /** - * An opaque reference to this dynamic texture. Can be used with [RenderContext.drawTexturedRect]. - */ - abstract val identifier: MyResourceLocation - - /** - * Destroy this texture. Using [identifier] after calling this will cause issues. - */ - fun destroy() { - if (!wasDestroyed) - doDestroy() - wasDestroyed = true - } - - abstract fun update(bufferedImage: BufferedImage) // TODO: check why this is broken on 1.21 - - protected abstract fun doDestroy() - - private var wasDestroyed = false - - override fun close() { - destroy() - } - - protected fun finalize() { // TODO: replace with a reference queue and Warnings.warn - if (!wasDestroyed) - IMinecraft.INSTANCE.getLogger("DynamicTextureReference").warn("Dangling DynamicTextureReference") - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.java new file mode 100644 index 000000000..30c613dde --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.java @@ -0,0 +1,22 @@ +package io.github.notenoughupdates.moulconfig.common; + +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; + +/** + * Not for manual implementation. This should be implemented by the corresponding platform. + * + * @see ForgeItemStack + */ +@ApiStatus.NonExtendable +public interface IItemStack { + List getLore(); + + StructuredText getDisplayName(); + + int getStackSize(); + + MyResourceLocation getItemId(); +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.kt deleted file mode 100644 index 3eb430458..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IItemStack.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import org.jetbrains.annotations.ApiStatus - -/** - * Not for manual implementation. This should be implemented by the corresponding platform. - * - * @see io.github.notenoughupdates.moulconfig.forge.ForgeItemStack - */ -@ApiStatus.NonExtendable -interface IItemStack { - fun getLore(): List - fun getDisplayName(): StructuredText - - fun getStackSize(): Int - fun getItemId(): MyResourceLocation -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.java new file mode 100644 index 000000000..ad0300733 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.java @@ -0,0 +1,33 @@ +package io.github.notenoughupdates.moulconfig.common; + +/** + * Not for manual implementation. This should be implemented by the corresponding platform. + * + * @see IMinecraft#getKeyboardConstants() + * @see KeyboardConstants + */ +public interface IKeyboardConstants { + int getBackSpace(); + int getCtrlLeft(); + int getCtrlRight(); + int getCmdLeft(); + int getCmdRight(); + int getShiftLeft(); + int getShiftRight(); + int getEscape(); + int getNone(); + int getEnter(); + int getDelete(); + int getUp(); + int getDown(); + int getRight(); + int getLeft(); + int getHome(); + int getEnd(); + int getKeyA(); + int getKeyC(); + int getKeyX(); + int getKeyV(); + int getKeyN(); + int getKeyF(); +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.kt deleted file mode 100644 index d6eb1bcb3..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IKeyboardConstants.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -/** - * Not for manual implementation. This should be implemented by the corresponding platform. - * @see IMinecraft.keyboardConstants - * @see KeyboardConstants - */ -interface IKeyboardConstants { - val backSpace: Int - val ctrlLeft: Int - val ctrlRight: Int - val cmdLeft: Int - val cmdRight: Int - val shiftLeft: Int - val shiftRight: Int - val escape: Int - val none: Int - val enter: Int - val delete: Int - val up: Int - val down: Int - val right: Int - val left: Int - val home: Int - val end: Int - val keyA: Int - val keyC: Int - val keyX: Int - val keyV: Int - val keyN: Int - val keyF: Int -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IMinecraft.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IMinecraft.java index 354fc0044..b175a9087 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IMinecraft.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/IMinecraft.java @@ -10,7 +10,6 @@ import io.github.notenoughupdates.moulconfig.internal.Warnings; import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor; import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; -import kotlin.Pair; import lombok.var; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; @@ -36,11 +35,11 @@ public interface IMinecraft { */ DynamicTextureReference generateDynamicTexture(BufferedImage image); - Pair getMousePositionHF(); + MoulConfigPair getMousePositionHF(); - default Pair getMousePosition() { + default MoulConfigPair getMousePosition() { var mousePositionHF = getMousePositionHF(); - return new Pair<>( + return new MoulConfigPair<>( mousePositionHF.getFirst().intValue(), mousePositionHF.getSecond().intValue()); } diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.java new file mode 100644 index 000000000..cc7f9f7ee --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.java @@ -0,0 +1,60 @@ +package io.github.notenoughupdates.moulconfig.common; + +public final class KeyboardConstants { + public static final KeyboardConstants INSTANCE = new KeyboardConstants(); + + private KeyboardConstants() { + } + + private static IKeyboardConstants constants() { + return IMinecraft.getInstance().getKeyboardConstants(); + } + + public static int getBackSpace() { return constants().getBackSpace(); } + public static int getCtrlLeft() { return constants().getCtrlLeft(); } + public static int getCtrlRight() { return constants().getCtrlRight(); } + public static int getCmdLeft() { return constants().getCmdLeft(); } + public static int getCmdRight() { return constants().getCmdRight(); } + public static int getShiftLeft() { return constants().getShiftLeft(); } + public static int getShiftRight() { return constants().getShiftRight(); } + public static int getEscape() { return constants().getEscape(); } + public static int getNone() { return constants().getNone(); } + public static int getEnter() { return constants().getEnter(); } + public static int getDelete() { return constants().getDelete(); } + public static int getUp() { return constants().getUp(); } + public static int getDown() { return constants().getDown(); } + public static int getRight() { return constants().getRight(); } + public static int getLeft() { return constants().getLeft(); } + public static int getHome() { return constants().getHome(); } + public static int getEnd() { return constants().getEnd(); } + public static int getKeyA() { return constants().getKeyA(); } + public static int getKeyC() { return constants().getKeyC(); } + public static int getKeyX() { return constants().getKeyX(); } + public static int getKeyV() { return constants().getKeyV(); } + public static int getKeyN() { return constants().getKeyN(); } + public static int getKeyF() { return constants().getKeyF(); } + + public int getBackSpaceValue() { return getBackSpace(); } + public int getCtrlLeftValue() { return getCtrlLeft(); } + public int getCtrlRightValue() { return getCtrlRight(); } + public int getCmdLeftValue() { return getCmdLeft(); } + public int getCmdRightValue() { return getCmdRight(); } + public int getShiftLeftValue() { return getShiftLeft(); } + public int getShiftRightValue() { return getShiftRight(); } + public int getEscapeValue() { return getEscape(); } + public int getNoneValue() { return getNone(); } + public int getEnterValue() { return getEnter(); } + public int getDeleteValue() { return getDelete(); } + public int getUpValue() { return getUp(); } + public int getDownValue() { return getDown(); } + public int getRightValue() { return getRight(); } + public int getLeftValue() { return getLeft(); } + public int getHomeValue() { return getHome(); } + public int getEndValue() { return getEnd(); } + public int getKeyAValue() { return getKeyA(); } + public int getKeyCValue() { return getKeyC(); } + public int getKeyXValue() { return getKeyX(); } + public int getKeyVValue() { return getKeyV(); } + public int getKeyNValue() { return getKeyN(); } + public int getKeyFValue() { return getKeyF(); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.kt deleted file mode 100644 index f48aba273..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/KeyboardConstants.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -object KeyboardConstants : IKeyboardConstants by IMinecraft.getInstance().keyboardConstants diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MoulConfigPair.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MoulConfigPair.java new file mode 100644 index 000000000..1ad24ecb7 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MoulConfigPair.java @@ -0,0 +1,39 @@ +package io.github.notenoughupdates.moulconfig.common; + +import java.util.Objects; + +public final class MoulConfigPair { + private final A first; + private final B second; + + public MoulConfigPair(A first, B second) { + this.first = first; + this.second = second; + } + + public A getFirst() { + return first; + } + + public B getSecond() { + return second; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MoulConfigPair)) return false; + MoulConfigPair that = (MoulConfigPair) o; + return Objects.equals(first, that.first) && Objects.equals(second, that.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.java new file mode 100644 index 000000000..7396c009a --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.java @@ -0,0 +1,56 @@ +package io.github.notenoughupdates.moulconfig.common; + +import java.util.Objects; + +public final class MyResourceLocation { + private final String root; + private final String path; + + public MyResourceLocation(String root, String path) { + if (root.contains(":")) { + throw new IllegalArgumentException("Root must not contain ':'"); + } + if (path.contains(":")) { + throw new IllegalArgumentException("Path must not contain ':'"); + } + this.root = root; + this.path = path; + } + + public String getRoot() { + return root; + } + + public String getPath() { + return path; + } + + public static MyResourceLocation parse(String string) { + String[] split = string.split(":", -1); + if (split.length == 1) { + return new MyResourceLocation("minecraft", split[0]); + } + if (split.length == 2) { + return new MyResourceLocation(split[0], split[1]); + } + throw new IllegalArgumentException("Resource location has to be in the format `namespace:path`, with `namespace:` being optional"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MyResourceLocation)) return false; + MyResourceLocation that = (MyResourceLocation) o; + return Objects.equals(root, that.root) && Objects.equals(path, that.path); + } + + @Override + public int hashCode() { + return Objects.hash(root, path); + } + + @Override + public String toString() { + return "MyResourceLocation(root=" + root + ", path=" + path + ")"; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.kt deleted file mode 100644 index be533c354..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/MyResourceLocation.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -data class MyResourceLocation(val root: String, val path: String) { - init { - require(":" !in root) - require(":" !in path) - } - - companion object { - fun parse(string: String): MyResourceLocation { - val s = string.split(":") - return when (s.size) { - 1 -> MyResourceLocation("minecraft", s[0]) - 2 -> MyResourceLocation(s[0], s[1]) - else -> error("Resource location has to be in the format `namespace:path`, with `namespace:` being optional") - } - } - } -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.java new file mode 100644 index 000000000..0362c70f2 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.java @@ -0,0 +1,35 @@ +package io.github.notenoughupdates.moulconfig.common; + +import io.github.notenoughupdates.moulconfig.GuiTextures; +import juuxel.libninepatch.NinePatch; + +public final class NinePatches { + public static final NinePatches INSTANCE = new NinePatches(); + + private NinePatches() { + } + + public static NinePatch createButton() { + return NinePatch.builder(GuiTextures.BUTTON) + .cornerSize(10) + .cornerUv(10 / 32F, 10 / 96F) + .mode(NinePatch.Mode.STRETCHING) + .build(); + } + + public static NinePatch createWhiteButton() { + return NinePatch.builder(GuiTextures.BUTTON_WHITE) + .cornerSize(14) + .cornerUv(14 / 32F, 14 / 96F) + .mode(NinePatch.Mode.STRETCHING) + .build(); + } + + public static NinePatch createVanillaPanel() { + return NinePatch.builder(GuiTextures.VANILLA_PANEL) + .cornerSize(4) + .cornerUv(4 / 16F) + .mode(NinePatch.Mode.STRETCHING) + .build(); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.kt deleted file mode 100644 index 3da235b09..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/common/NinePatches.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -import io.github.notenoughupdates.moulconfig.GuiTextures -import juuxel.libninepatch.NinePatch - -object NinePatches { - fun createButton(): NinePatch { - return NinePatch.builder(GuiTextures.BUTTON) - .cornerSize(10) - .cornerUv(10 / 32f, 10 / 96F) - .mode(NinePatch.Mode.STRETCHING) - .build() - } - fun createWhiteButton(): NinePatch { - return NinePatch.builder(GuiTextures.BUTTON_WHITE) - .cornerSize(14) - .cornerUv(14 / 32f, 14 / 96F) - .mode(NinePatch.Mode.STRETCHING) - .build() - } - - fun createVanillaPanel(): NinePatch { - return NinePatch.builder(GuiTextures.VANILLA_PANEL) - .cornerSize(4) - .cornerUv(4 / 16F) - .mode(NinePatch.Mode.STRETCHING) - .build() - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.java new file mode 100644 index 000000000..ef26acd3e --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.java @@ -0,0 +1,36 @@ +package io.github.notenoughupdates.moulconfig.gui; + +import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +public class ClassResizableTextField extends TextFieldComponent { + private int width = 20; + + public ClassResizableTextField(GetSetter text) { + super(text, 20); + } + + public void setWidth(int width) { + this.width = width; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public void render(GuiImmediateContext context) { + super.render(context.translated(0, 0, width, 18)); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + return super.mouseEvent(mouseEvent, context.translated(0, 0, width, 18)); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + return super.keyboardEvent(event, context.translated(0, 0, width, 18)); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.kt deleted file mode 100644 index 4932398d6..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/ClassResizableTextField.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui - -import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter - -class ClassResizableTextField(text: GetSetter) : TextFieldComponent( - text, - 20, -) { - private var width = 20 - - fun setWidth(width: Int) { - this.width = width - } - - override fun getWidth(): Int { - return width - } - - override fun render(context: GuiImmediateContext) { - super.render(context.translated(0, 0, width, 18)) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - return super.mouseEvent(mouseEvent, context.translated(0, 0, width, 18)) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - return super.keyboardEvent(event, context.translated(0, 0, width, 18)) - } - -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.java new file mode 100644 index 000000000..27f2e7e51 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.java @@ -0,0 +1,32 @@ +package io.github.notenoughupdates.moulconfig.gui; + +/** + * Implement this interface alongside {@link GuiComponent} to receive an event when the gui closes. + */ +public interface CloseEventListener { + enum CloseAction { + NO_OBJECTIONS_TO_CLOSE, + DENY_CLOSE; + + public CloseAction or(CloseAction other) { + if (this == DENY_CLOSE) { + return this; + } + return other; + } + } + + /** + * Called just before a voluntary close. Return {@link CloseAction#DENY_CLOSE} to override the close. Make sure to update + * your state such that the user can close the gui afterward. + */ + default CloseAction onBeforeClose() { + return CloseAction.NO_OBJECTIONS_TO_CLOSE; + } + + /** + * Called after the gui has been closed, both by the component gui, or by other actors, such as a teleport packet. + */ + default void onAfterClose() { + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.kt deleted file mode 100644 index 38c5f3d4f..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/CloseEventListener.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui - -/** - * Implement this interface alongside [GuiComponent] to receive an event when the gui closes. - */ -interface CloseEventListener { - enum class CloseAction { - NO_OBJECTIONS_TO_CLOSE, DENY_CLOSE, ; - - fun or(other: CloseAction): CloseAction { - if (this == DENY_CLOSE) return this - return other - } - } - - /** - * Called just before a voluntary close. Return [CloseAction.DENY_CLOSE] to override the close. Make sure to update - * your state such that the user can close the gui afterward. - */ - fun onBeforeClose(): CloseAction = CloseAction.NO_OBJECTIONS_TO_CLOSE - - /** - * Called after the gui has been closed, both by the component gui, or by other actors, such as a teleport packet. - */ - fun onAfterClose() = Unit -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.java new file mode 100644 index 000000000..f6c6616a2 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of MoulConfig. + * + * MoulConfig is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * MoulConfig is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with MoulConfig. If not, see . + */ +package io.github.notenoughupdates.moulconfig.gui; + +import io.github.notenoughupdates.moulconfig.common.RenderContext; + +import java.util.Objects; + +/** + * A context containing the constraints of a gui elements, as well as the state of the user interface, relative to that gui element. + */ +public final class GuiImmediateContext { + private final RenderContext renderContext; + private final int renderOffsetX; + private final int renderOffsetY; + private final int width; + private final int height; + private final int mouseX; + private final int mouseY; + private final int absoluteMouseX; + private final int absoluteMouseY; + private final float mouseXHF; + private final float mouseYHF; + + public GuiImmediateContext( + RenderContext renderContext, + int renderOffsetX, + int renderOffsetY, + int width, + int height, + int mouseX, + int mouseY, + int absoluteMouseX, + int absoluteMouseY, + float mouseXHF, + float mouseYHF + ) { + this.renderContext = renderContext; + this.renderOffsetX = renderOffsetX; + this.renderOffsetY = renderOffsetY; + this.width = width; + this.height = height; + this.mouseX = mouseX; + this.mouseY = mouseY; + this.absoluteMouseX = absoluteMouseX; + this.absoluteMouseY = absoluteMouseY; + this.mouseXHF = mouseXHF; + this.mouseYHF = mouseYHF; + } + + public RenderContext getRenderContext() { + return renderContext; + } + + /** + * The current absolute offset for this gui context. This should not need to be accessed, unless you are contacting some API that does not access GlStateManager. + */ + public int getRenderOffsetX() { + return renderOffsetX; + } + + /** + * The current absolute offset for this gui context. This should not need to be accessed, unless you are contacting some API that does not access GlStateManager. + */ + public int getRenderOffsetY() { + return renderOffsetY; + } + + /** + * The available width for that gui element to render in. + */ + public int getWidth() { + return width; + } + + /** + * The available height for that gui element to render in. + */ + public int getHeight() { + return height; + } + + /** + * The position of the mouse, relative to this gui element. + */ + public int getMouseX() { + return mouseX; + } + + /** + * The position of the mouse, relative to this gui element. + */ + public int getMouseY() { + return mouseY; + } + + /** + * The position of the mouse, relative to the root element. + */ + public int getAbsoluteMouseX() { + return absoluteMouseX; + } + + /** + * The position of the mouse, relative to the root element. + */ + public int getAbsoluteMouseY() { + return absoluteMouseY; + } + + /** + * The position of the mouse, relative to this gui element in as high of a resolution as possible. + */ + public float getMouseXHF() { + return mouseXHF; + } + + /** + * The position of the mouse, relative to this gui element in as high of a resolution as possible. + */ + public float getMouseYHF() { + return mouseYHF; + } + + /** + * Check if the mouse is positioned within this context. + */ + public boolean isHovered() { + return mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height; + } + + /** + * Construct a new context that bleeds out over the boundaries of the existing context. + * This is usually used for more fuzzy click detection. + * + * @param xBleed extra size in the negative and positive x direction + * @param yBleed extra size in the negative and positive y direction + */ + public GuiImmediateContext withBleed(int xBleed, int yBleed) { + return new GuiImmediateContext( + renderContext, + renderOffsetX - xBleed, + renderOffsetY - yBleed, + width + 2 * xBleed, + height + 2 * yBleed, + mouseX + xBleed, + mouseY + yBleed, + absoluteMouseX, + absoluteMouseY, + mouseXHF, + mouseYHF + ); + } + + /** + * Construct a new context representing that is located within this context. + * + * @param xOffset relative x position of the new context in the current context + * @param yOffset relative y position of the new context in the current context + * @param width width of the new sub context + * @param height height of the new sub context + */ + public GuiImmediateContext translated(int xOffset, int yOffset, int width, int height) { + return new GuiImmediateContext( + renderContext, + renderOffsetX + xOffset, + renderOffsetY + yOffset, + width, + height, + mouseX - xOffset, + mouseY - yOffset, + absoluteMouseX, + absoluteMouseY, + mouseXHF - xOffset, + mouseYHF - yOffset + ); + } + + /** + * Construct a new context representing that is located within this context. Does not translate the rendering offset. + * + * @param xOffset relative x position of the new context in the current context + * @param yOffset relative y position of the new context in the current context + * @param width width of the new sub context + * @param height height of the new sub context + */ + public GuiImmediateContext translatedNonRendering(int xOffset, int yOffset, int width, int height) { + return new GuiImmediateContext( + renderContext, + renderOffsetX, + renderOffsetY, + width, + height, + mouseX - xOffset, + mouseY - yOffset, + absoluteMouseX, + absoluteMouseY, + mouseXHF, + mouseYHF + ); + } + + /** + * Construct a new context, which has not been translated, but possible smaller if the arguments demand so. + * + * @param maxWidth max width of the new context. this argument will be ignored if it is larger than the current width + * @param maxHeight max height of the new context. this argument will be ignored if it is larger than the current height + */ + public GuiImmediateContext limitSize(int maxWidth, int maxHeight) { + return translated(0, 0, Math.min(width, maxWidth), Math.min(height, maxHeight)); + } + + public GuiImmediateContext scaled(float scale) { + return new GuiImmediateContext( + renderContext, + renderOffsetX, + renderOffsetY, + (int) (width / scale), + (int) (height / scale), + (int) ((mouseX - renderOffsetX) * scale), + (int) ((mouseY - renderOffsetY) * scale), + absoluteMouseX, + absoluteMouseY, + (mouseXHF - renderOffsetX) * scale, + (mouseYHF - renderOffsetY) * scale + ); + } + + public GuiImmediateContext copy( + RenderContext renderContext, + int renderOffsetX, + int renderOffsetY, + int width, + int height, + int mouseX, + int mouseY, + int absoluteMouseX, + int absoluteMouseY, + float mouseXHF, + float mouseYHF + ) { + return new GuiImmediateContext(renderContext, renderOffsetX, renderOffsetY, width, height, mouseX, mouseY, absoluteMouseX, absoluteMouseY, mouseXHF, mouseYHF); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GuiImmediateContext)) return false; + GuiImmediateContext that = (GuiImmediateContext) o; + return renderOffsetX == that.renderOffsetX + && renderOffsetY == that.renderOffsetY + && width == that.width + && height == that.height + && mouseX == that.mouseX + && mouseY == that.mouseY + && absoluteMouseX == that.absoluteMouseX + && absoluteMouseY == that.absoluteMouseY + && Float.compare(that.mouseXHF, mouseXHF) == 0 + && Float.compare(that.mouseYHF, mouseYHF) == 0 + && Objects.equals(renderContext, that.renderContext); + } + + @Override + public int hashCode() { + return Objects.hash(renderContext, renderOffsetX, renderOffsetY, width, height, mouseX, mouseY, absoluteMouseX, absoluteMouseY, mouseXHF, mouseYHF); + } + + @Override + public String toString() { + return "GuiImmediateContext(renderContext=" + renderContext + + ", renderOffsetX=" + renderOffsetX + + ", renderOffsetY=" + renderOffsetY + + ", width=" + width + + ", height=" + height + + ", mouseX=" + mouseX + + ", mouseY=" + mouseY + + ", absoluteMouseX=" + absoluteMouseX + + ", absoluteMouseY=" + absoluteMouseY + + ", mouseXHF=" + mouseXHF + + ", mouseYHF=" + mouseYHF + ")"; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.kt deleted file mode 100644 index fbf61aa15..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/GuiImmediateContext.kt +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2023 NotEnoughUpdates contributors - * - * This file is part of MoulConfig. - * - * MoulConfig is free software: you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * MoulConfig is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with MoulConfig. If not, see . - * - */ -package io.github.notenoughupdates.moulconfig.gui - -import io.github.notenoughupdates.moulconfig.common.RenderContext - -/** - * A context containing the constraints of a gui elements, as well as the state of the user interface, relative to that gui element. - */ -data class GuiImmediateContext constructor( - val renderContext: RenderContext, - - /** - * The current absolute offset for this gui context. This should not need to be accessed, unless you are contacting some API that does not access GlStateManager. - */ - val renderOffsetX: Int, - - /** - * The current absolute offset for this gui context. This should not need to be accessed, unless you are contacting some API that does not access GlStateManager. - */ - val renderOffsetY: Int, - - /** - * The available width for that gui element to render in. - */ - val width: Int, - - /** - * The available height for that gui element to render in. - */ - val height: Int, - - /** - * The position of the mouse, relative to this gui element. - */ - val mouseX: Int, - - /** - * The position of the mouse, relative to this gui element. - */ - val mouseY: Int, - - /** - * The position of the mouse, relative to the root element. - */ - val absoluteMouseX: Int, - - /** - * The position of the mouse, relative to the root element. - */ - val absoluteMouseY: Int, - - /** - * The position of the mouse, relative to this gui element in as high of a resolution as possible. - */ - val mouseXHF: Float, - - /** - * The position of the mouse, relative to this gui element in as high of a resolution as possible. - */ - val mouseYHF: Float, -) { - val isHovered: Boolean - /** - * Check if the mouse is positioned within this context. - */ - get() = mouseX in 0 until width && mouseY in 0 until height - - /** - * Construct a new context that bleeds out over the boundaries of the existing context. - * This is usually used for more fuzzy click detection. - * - * @param xBleed extra size in the negative and positive x direction - * @param yBleed extra size in the negative and positive y direction - */ - fun withBleed(xBleed: Int, yBleed: Int): GuiImmediateContext { - return GuiImmediateContext( - renderContext, - renderOffsetX - xBleed, renderOffsetY - yBleed, width + 2 * xBleed, height + 2 * yBleed, - mouseX + xBleed, mouseY + yBleed, - absoluteMouseX, absoluteMouseY, - mouseXHF, mouseYHF - ) - } - - /** - * Construct a new context representing that is located within this context. - * - * @param xOffset relative x position of the new context in the current context - * @param yOffset relative y position of the new context in the current context - * @param width width of the new sub context - * @param height height of the new sub context - */ - fun translated(xOffset: Int, yOffset: Int, width: Int, height: Int): GuiImmediateContext { - return GuiImmediateContext( - renderContext, - renderOffsetX + xOffset, - renderOffsetY + yOffset, - width, - height, - mouseX - xOffset, - mouseY - yOffset, - absoluteMouseX, - absoluteMouseY, - mouseXHF - xOffset, - mouseYHF - yOffset - ) - } - - /** - * Construct a new context representing that is located within this context. Does not translate the rendering offset. - * - * @param xOffset relative x position of the new context in the current context - * @param yOffset relative y position of the new context in the current context - * @param width width of the new sub context - * @param height height of the new sub context - */ - fun translatedNonRendering(xOffset: Int, yOffset: Int, width: Int, height: Int): GuiImmediateContext { - return GuiImmediateContext( - renderContext, renderOffsetX, renderOffsetY, width, height, mouseX - xOffset, mouseY - yOffset, - absoluteMouseX, absoluteMouseY, - mouseXHF, - mouseYHF - ) - } - - /** - * Construct a new context, which has not been translated, but possible smaller if the arguments demand so. - * - * @param maxWidth max width of the new context. this argument will be ignored if it is larger than the current width - * @param maxHeight max height of the new context. this argument will be ignored if it is larger than the current height - */ - fun limitSize(maxWidth: Int, maxHeight: Int): GuiImmediateContext { - return translated(0, 0, minOf(width, maxWidth), minOf(height, maxHeight)) - } - - fun scaled(scale: Float): GuiImmediateContext { - return GuiImmediateContext( - renderContext, - renderOffsetX, renderOffsetY, - (width / scale).toInt(), (height / scale).toInt(), - ((mouseX - renderOffsetX) * scale).toInt(), ((mouseY - renderOffsetY) * scale).toInt(), - absoluteMouseX, absoluteMouseY, - ((mouseXHF - renderOffsetX) * scale), - ((mouseYHF - renderOffsetY) * scale), - ) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.java new file mode 100644 index 000000000..eb98d6114 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.java @@ -0,0 +1,97 @@ +package io.github.notenoughupdates.moulconfig.gui; + +import java.util.Objects; + +public interface KeyboardEvent { + final class CharTyped implements KeyboardEvent { + private final char character; + + public CharTyped(char character) { + this.character = character; + } + + public char getChar() { + return character; + } + + public char component1() { + return character; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CharTyped)) return false; + CharTyped charTyped = (CharTyped) o; + return character == charTyped.character; + } + + @Override + public int hashCode() { + return Character.hashCode(character); + } + + @Override + public String toString() { + return "CharTyped(char=" + character + ")"; + } + } + + final class KeyPressed implements KeyboardEvent { + private final int keycode; + private final int scancode; + private final boolean pressed; + + public KeyPressed(int keycode, int scancode, boolean pressed) { + this.keycode = keycode; + this.scancode = scancode; + this.pressed = pressed; + } + + public int getKeycode() { + return keycode; + } + + public int getScancode() { + return scancode; + } + + public boolean getPressed() { + return pressed; + } + + public boolean isPressed() { + return pressed; + } + + public int component1() { + return keycode; + } + + public int component2() { + return scancode; + } + + public boolean component3() { + return pressed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof KeyPressed)) return false; + KeyPressed that = (KeyPressed) o; + return keycode == that.keycode && scancode == that.scancode && pressed == that.pressed; + } + + @Override + public int hashCode() { + return Objects.hash(keycode, scancode, pressed); + } + + @Override + public String toString() { + return "KeyPressed(keycode=" + keycode + ", scancode=" + scancode + ", pressed=" + pressed + ")"; + } + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.kt deleted file mode 100644 index 5a35cf88b..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/KeyboardEvent.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui - -sealed interface KeyboardEvent { - data class CharTyped(val char: Char) : KeyboardEvent - data class KeyPressed(val keycode: Int, val scancode: Int, val pressed: Boolean) : KeyboardEvent -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.java new file mode 100644 index 000000000..74a7cab5a --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.java @@ -0,0 +1,127 @@ +package io.github.notenoughupdates.moulconfig.gui; + +import java.util.Objects; + +public interface MouseEvent { + final class Click implements MouseEvent { + private final int mouseButton; + private final boolean mouseState; + + public Click(int mouseButton, boolean mouseState) { + this.mouseButton = mouseButton; + this.mouseState = mouseState; + } + + public int getMouseButton() { + return mouseButton; + } + + public boolean getMouseState() { + return mouseState; + } + + public int component1() { + return mouseButton; + } + + public boolean component2() { + return mouseState; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Click)) return false; + Click click = (Click) o; + return mouseButton == click.mouseButton && mouseState == click.mouseState; + } + + @Override + public int hashCode() { + return Objects.hash(mouseButton, mouseState); + } + + @Override + public String toString() { + return "Click(mouseButton=" + mouseButton + ", mouseState=" + mouseState + ")"; + } + } + + final class Move implements MouseEvent { + private final float dx; + private final float dy; + + public Move(float dx, float dy) { + this.dx = dx; + this.dy = dy; + } + + public float getDx() { + return dx; + } + + public float getDy() { + return dy; + } + + public float component1() { + return dx; + } + + public float component2() { + return dy; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Move)) return false; + Move move = (Move) o; + return Float.compare(move.dx, dx) == 0 && Float.compare(move.dy, dy) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(dx, dy); + } + + @Override + public String toString() { + return "Move(dx=" + dx + ", dy=" + dy + ")"; + } + } + + final class Scroll implements MouseEvent { + private final float dWheel; + + public Scroll(float dWheel) { + this.dWheel = dWheel; + } + + public float getDWheel() { + return dWheel; + } + + public float component1() { + return dWheel; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Scroll)) return false; + Scroll scroll = (Scroll) o; + return Float.compare(scroll.dWheel, dWheel) == 0; + } + + @Override + public int hashCode() { + return Float.hashCode(dWheel); + } + + @Override + public String toString() { + return "Scroll(dWheel=" + dWheel + ")"; + } + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.kt deleted file mode 100644 index d68e2512a..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/MouseEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui - -sealed interface MouseEvent { - data class Click(val mouseButton: Int, val mouseState: Boolean) : MouseEvent - data class Move(val dx: Float, val dy: Float) : MouseEvent - data class Scroll(val dWheel: Float) : MouseEvent -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.java new file mode 100644 index 000000000..5e195332f --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.java @@ -0,0 +1,7 @@ +package io.github.notenoughupdates.moulconfig.gui; + +public enum VerticalAlign { + BOTTOM, + CENTER, + TOP +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.kt deleted file mode 100644 index 0a889249e..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/VerticalAlign.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui - -enum class VerticalAlign { - BOTTOM, CENTER, TOP, -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.java new file mode 100644 index 000000000..ac705ee7c --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.java @@ -0,0 +1,90 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.HorizontalAlign; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.gui.VerticalAlign; + +import java.util.function.BiFunction; +import java.util.function.Supplier; + +public class AlignComponent extends GuiComponent { + private final GuiComponent child; + private final Supplier horizontal; + private final Supplier vertical; + + public AlignComponent(GuiComponent child, Supplier horizontal, Supplier vertical) { + this.child = child; + this.horizontal = horizontal; + this.vertical = vertical; + } + + public GuiComponent getChild() { return child; } + public Supplier getHorizontal() { return horizontal; } + public Supplier getVertical() { return vertical; } + + @Override + public int getWidth() { + return child.getWidth(); + } + + @Override + public int getHeight() { + return child.getHeight(); + } + + public GuiImmediateContext getChildContext(GuiImmediateContext context) { + return context.translated(getChildOffsetX(context), getChildOffsetY(context), child.getWidth(), child.getHeight()); + } + + public int getChildOffsetX(GuiImmediateContext context) { + switch (horizontal.get()) { + case LEFT: + return 0; + case CENTER: + return context.getWidth() / 2 - child.getWidth() / 2; + case RIGHT: + return context.getWidth() - child.getWidth(); + default: + throw new IllegalStateException("Unknown horizontal alignment"); + } + } + + public int getChildOffsetY(GuiImmediateContext context) { + switch (vertical.get()) { + case BOTTOM: + return context.getHeight() - child.getHeight(); + case CENTER: + return context.getHeight() / 2 - child.getHeight() / 2; + case TOP: + return 0; + default: + throw new IllegalStateException("Unknown vertical alignment"); + } + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + return visitor.apply(child, initial); + } + + @Override + public void render(GuiImmediateContext context) { + context.getRenderContext().pushMatrix(); + context.getRenderContext().translate((float) getChildOffsetX(context), (float) getChildOffsetY(context)); + child.render(getChildContext(context)); + context.getRenderContext().popMatrix(); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + return child.keyboardEvent(event, getChildContext(context)); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + return child.mouseEvent(mouseEvent, getChildContext(context)); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.kt deleted file mode 100644 index 00de42a8b..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/AlignComponent.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.HorizontalAlign -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.gui.VerticalAlign -import java.util.function.BiFunction -import java.util.function.Supplier - -data class AlignComponent( - val child: GuiComponent, - val horizontal: Supplier, - val vertical: Supplier, -) : GuiComponent() { - override fun getWidth(): Int { - return child.width - } - - override fun getHeight(): Int { - return child.height - } - - fun getChildContext(context: GuiImmediateContext): GuiImmediateContext { - return context.translated( - getChildOffsetX(context), - getChildOffsetY(context), - child.width, - child.height - ) - } - - fun getChildOffsetX(context: GuiImmediateContext): Int { - return when (horizontal.get()) { - HorizontalAlign.LEFT -> 0 - HorizontalAlign.CENTER -> context.width / 2 - child.width / 2 - HorizontalAlign.RIGHT -> context.width - child.width - } - } - - fun getChildOffsetY(context: GuiImmediateContext): Int { - return when (vertical.get()) { - VerticalAlign.BOTTOM -> context.height - child.height - VerticalAlign.CENTER -> context.height / 2 - child.height / 2 - VerticalAlign.TOP -> 0 - } - } - - override fun foldChildren(initial: T, visitor: BiFunction): T { - return visitor.apply(child, initial) - } - - override fun render(context: GuiImmediateContext) { - context.renderContext.pushMatrix() - context.renderContext.translate(getChildOffsetX(context).toFloat(), getChildOffsetY(context).toFloat()) - child.render(getChildContext(context)) - context.renderContext.popMatrix() - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - return child.keyboardEvent(event, getChildContext(context)) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - return child.mouseEvent(mouseEvent, getChildContext(context)) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ArrayComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ArrayComponent.java index 4084aa246..809f00df7 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ArrayComponent.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ArrayComponent.java @@ -1,12 +1,12 @@ package io.github.notenoughupdates.moulconfig.gui.component; +import io.github.notenoughupdates.moulconfig.common.MoulConfigPair; import io.github.notenoughupdates.moulconfig.gui.GuiComponent; import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; import io.github.notenoughupdates.moulconfig.gui.MouseEvent; import io.github.notenoughupdates.moulconfig.observer.GetSetter; import io.github.notenoughupdates.moulconfig.observer.ObservableList; -import kotlin.Pair; import lombok.Getter; import java.awt.Color; @@ -69,9 +69,9 @@ public T foldChildren(T initial, BiFunction visitor) { } public void foldWithContext(GuiImmediateContext context, ContextVisitor visitor) { - foldChildren(new Pair<>(0, 0), (child, position) -> { + foldChildren(new MoulConfigPair<>(0, 0), (child, position) -> { visitor.onContext(child, context.translated(0, position.getFirst(), child.getWidth(), child.getHeight()), position.getSecond()); - return new Pair<>(child.getHeight() + position.getFirst(), position.getSecond() + 1); + return new MoulConfigPair<>(child.getHeight() + position.getFirst(), position.getSecond() + 1); }); } diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.java new file mode 100644 index 000000000..f54218466 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.java @@ -0,0 +1,48 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.KeyboardConstants; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; + +public class ButtonComponent extends PanelComponent { + private final Runnable onClick; + + public ButtonComponent(GuiComponent element, int insets, Runnable onClick, BackgroundRenderer panel) { + super(element, insets, panel); + this.onClick = onClick; + } + + public ButtonComponent(GuiComponent element, int insets, Runnable onClick) { + this(element, insets, onClick, DefaultBackgroundRenderer.DARK_RECT); + } + + public Runnable getOnClick() { + return onClick; + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + if (context.isHovered() && mouseEvent instanceof MouseEvent.Click) { + MouseEvent.Click click = (MouseEvent.Click) mouseEvent; + if (click.getMouseState() && click.getMouseButton() == 0) { + onClick.run(); + return true; + } + } + return false; + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + if (isFocused() && event instanceof KeyboardEvent.KeyPressed) { + KeyboardEvent.KeyPressed keyPressed = (KeyboardEvent.KeyPressed) event; + if (keyPressed.getPressed() && keyPressed.getKeycode() == KeyboardConstants.INSTANCE.getEnter()) { + onClick.run(); + return true; + } + } + return false; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.kt deleted file mode 100644 index 8a2811e20..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ButtonComponent.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.KeyboardConstants -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent.Click - -class ButtonComponent @JvmOverloads constructor( - element: GuiComponent, - insets: Int, - val onClick: Runnable, - panel: BackgroundRenderer = DefaultBackgroundRenderer.DARK_RECT -) : PanelComponent(element, insets, panel) { - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - if (context.isHovered && mouseEvent is Click) { - val (mouseButton, mouseState) = mouseEvent - if (mouseState && mouseButton == 0) { - onClick.run() - return true - } - } - return false - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - if (isFocused && event is KeyboardEvent.KeyPressed && - event.pressed && event.keycode == KeyboardConstants.enter - ) { - onClick.run() - return true - } - return false - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.java new file mode 100644 index 000000000..8ab1e774c --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.java @@ -0,0 +1,82 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +import java.util.function.Supplier; + +public class CollapsibleComponent extends GuiComponent { + public static final IFontRenderer fr = IMinecraft.INSTANCE.getDefaultFontRenderer(); + public static final int padding = 2; + public static final int trim = 3; + public static final int iconWidth = 9; + + private final Supplier title; + private final Supplier body; + private final GetSetter collapsedState; + + public CollapsibleComponent(Supplier title, Supplier body) { + this(title, body, GetSetter.floating(true)); + } + + public CollapsibleComponent(Supplier title, Supplier body, GetSetter collapsedState) { + this.title = title; + this.body = body; + this.collapsedState = collapsedState; + } + + @Override + public int getWidth() { + return Math.max(title.get().getWidth() + padding + iconWidth, body.get().getWidth()); + } + + @Override + public int getHeight() { + int barHeight = Math.max(title.get().getHeight(), fr.getHeight()); + return collapsedState.get() ? barHeight : barHeight + trim + body.get().getHeight(); + } + + @Override + public void render(GuiImmediateContext context) { + boolean collapsed = collapsedState.get(); + context.getRenderContext().drawOpenCloseTriangle(!collapsed, 0F, 0F, (float) iconWidth, (float) iconWidth, -1); + int barHeight = Math.max(title.get().getHeight(), fr.getHeight()); + context.getRenderContext().pushMatrix(); + context.getRenderContext().translate((float) iconWidth, 0F); + title.get().render(context.translated(iconWidth, 0, context.getWidth() - iconWidth, barHeight)); + context.getRenderContext().popMatrix(); + + if (!collapsed) { + context.getRenderContext().drawColoredRect(0F, barHeight + 1F, (float) context.getWidth(), barHeight + 2F, 0xFF000000); + context.getRenderContext().pushMatrix(); + context.getRenderContext().translate(0F, (float) barHeight); + body.get().render(context.translated(0, barHeight, context.getWidth(), context.getHeight() - barHeight)); + context.getRenderContext().popMatrix(); + } + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + int barHeight = Math.max(title.get().getHeight(), fr.getHeight()); + if (mouseEvent instanceof MouseEvent.Click && context.translated(0, 0, context.getWidth(), barHeight).isHovered()) { + if (((MouseEvent.Click) mouseEvent).getMouseState()) { + collapsedState.set(!collapsedState.get()); + } + return true; + } + return title.get().mouseEvent(mouseEvent, context.translated(iconWidth, 0, context.getWidth() - iconWidth, barHeight)) + || body.get().mouseEvent(mouseEvent, context.translated(0, barHeight, context.getWidth(), context.getHeight() - barHeight)); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + int barHeight = Math.max(title.get().getHeight(), fr.getHeight()); + return title.get().keyboardEvent(event, context.translated(iconWidth, 0, context.getWidth() - iconWidth, barHeight)) + || body.get().keyboardEvent(event, context.translated(0, barHeight, context.getWidth(), context.getHeight() - barHeight)); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.kt deleted file mode 100644 index 8df064701..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/CollapsibleComponent.kt +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import java.util.function.Supplier - -class CollapsibleComponent( - val title: Supplier, - val body: Supplier, - val collapsedState: GetSetter = GetSetter.floating( - true - ) -) : GuiComponent() { - - companion object { - val fr = IMinecraft.INSTANCE.defaultFontRenderer - val padding = 2 - val trim = 3 - val iconWidth = 9 - } - - override fun getWidth(): Int { - return maxOf(title.get().width + padding + iconWidth, body.get().width) - } - - override fun getHeight(): Int { - return if (collapsedState.get()) { - maxOf(title.get().height, fr.height) - } else { - maxOf(title.get().height, fr.height) + trim + body.get().height - } - } - - override fun render(context: GuiImmediateContext) { - val collapsed = collapsedState.get() - context.renderContext.drawOpenCloseTriangle(!collapsed, 0F, 0F, iconWidth.toFloat(), iconWidth.toFloat(), -1) - val barHeight = maxOf(title.get().height, fr.height) - context.renderContext.pushMatrix() - context.renderContext.translate(iconWidth.toFloat(), 0F) - title.get().render(context.translated(iconWidth, 0, context.width - iconWidth, barHeight)) - context.renderContext.popMatrix() - - if (!collapsed) { - context.renderContext.drawColoredRect( - 0F, - barHeight + 1F, - context.width.toFloat(), - barHeight + 2F, - 0xFF000000.toInt() - ) - - context.renderContext.pushMatrix() - context.renderContext.translate(0F, barHeight.toFloat()) - body.get().render( - context.translated(0, barHeight, context.width, context.height - barHeight) - ) - context.renderContext.popMatrix() - } - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - val barHeight = maxOf(title.get().height, fr.height) - - if (mouseEvent is MouseEvent.Click && context.translated( - 0, - 0, - context.width, - barHeight - ).isHovered - ) { - if (mouseEvent.mouseState) - collapsedState.set(!collapsedState.get()) - return true - } - - return title.get().mouseEvent( - mouseEvent, context.translated( - iconWidth, - 0, - context.width - iconWidth, - barHeight - ) - ) || body.get().mouseEvent( - mouseEvent, context.translated(0, barHeight, context.width, context.height - barHeight) - ) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - val barHeight = maxOf(title.get().height, fr.height) - return title.get().keyboardEvent( - event, context.translated( - iconWidth, - 0, - context.width - iconWidth, - barHeight - ) - ) || body.get().keyboardEvent( - event, context.translated(0, barHeight, context.width, context.height - barHeight) - ) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.java new file mode 100644 index 000000000..6f55dd378 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.java @@ -0,0 +1,57 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +public class HoverComponent extends GuiComponent { + private final GuiComponent child; + private final Supplier> hoverLines; + + public HoverComponent(GuiComponent child, Supplier> hoverLines) { + this.child = child; + this.hoverLines = hoverLines; + } + + public GuiComponent getChild() { return child; } + public Supplier> getHoverLines() { return hoverLines; } + + @Override + public int getWidth() { + return child.getWidth(); + } + + @Override + public int getHeight() { + return child.getHeight(); + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + return visitor.apply(child, initial); + } + + @Override + public void render(GuiImmediateContext context) { + if (context.isHovered()) { + context.getRenderContext().scheduleDrawTooltip(context.getMouseX(), context.getMouseY(), hoverLines.get()); + } + child.render(context); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + return child.mouseEvent(mouseEvent, context); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + return child.keyboardEvent(event, context); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.kt deleted file mode 100644 index 47d7684bb..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/HoverComponent.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import java.util.function.BiFunction -import java.util.function.Supplier - -class HoverComponent( - val child: GuiComponent, - val hoverLines: Supplier>, -) : GuiComponent() { - override fun getWidth(): Int { - return child.width - } - - override fun getHeight(): Int { - return child.height - } - - override fun foldChildren( - initial: T, - visitor: BiFunction - ): T { - return visitor.apply(child, initial) - } - - override fun render(context: GuiImmediateContext) { - if (context.isHovered) { - context.renderContext.scheduleDrawTooltip(context.mouseX, context.mouseY, hoverLines.get()) - } - child.render(context) - - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - return child.mouseEvent(mouseEvent, context) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - return child.keyboardEvent(event, context) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.java new file mode 100644 index 000000000..848c7c425 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.java @@ -0,0 +1,51 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; + +import java.util.function.BiFunction; +import java.util.function.Supplier; + +public class IndirectComponent extends GuiComponent { + private final Supplier component; + + public IndirectComponent(Supplier component) { + this.component = component; + } + + public Supplier getComponent() { + return component; + } + + @Override + public int getWidth() { + return component.get().getWidth(); + } + + @Override + public int getHeight() { + return component.get().getHeight(); + } + + @Override + public void render(GuiImmediateContext context) { + component.get().render(context); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + return component.get().keyboardEvent(event, context); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + return component.get().mouseEvent(mouseEvent, context); + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + return visitor.apply(component.get(), initial); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.kt deleted file mode 100644 index de818747c..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/IndirectComponent.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import java.util.function.BiFunction -import java.util.function.Supplier - -open class IndirectComponent( - val component: Supplier -) : GuiComponent() { - override fun getWidth(): Int { - return component.get().width - } - - override fun getHeight(): Int { - return component.get().height - } - - override fun render(context: GuiImmediateContext) { - component.get().render(context) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - return component.get().keyboardEvent(event, context) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - return component.get().mouseEvent(mouseEvent, context) - } - - override fun foldChildren( - initial: T, - visitor: BiFunction - ): T { - return visitor.apply(component.get(), initial) - } -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.java new file mode 100644 index 000000000..21d34ebb1 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.java @@ -0,0 +1,34 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.IItemStack; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +public class ItemStackComponent extends GuiComponent { + private final GetSetter itemStack; + + public ItemStackComponent(GetSetter itemStack) { + this.itemStack = itemStack; + } + + public GetSetter getItemStack() { + return itemStack; + } + + @Override + public int getWidth() { + return 18; + } + + @Override + public int getHeight() { + return 18; + } + + @Override + public void render(GuiImmediateContext context) { + context.getRenderContext().renderItemStack(itemStack.get(), 1, 1, StructuredText.empty()); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.kt deleted file mode 100644 index c26377df2..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ItemStackComponent.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.IItemStack -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.observer.GetSetter - -open class ItemStackComponent( - val itemStack: GetSetter, -) : GuiComponent() { - override fun getWidth(): Int { - return 18 - } - - override fun getHeight(): Int { - return 18 - } - - override fun render(context: GuiImmediateContext) { - context.renderContext.renderItemStack(itemStack.get(), 1, 1, StructuredText.empty()) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.java new file mode 100644 index 000000000..cf406629a --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.java @@ -0,0 +1,67 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.CloseEventListener; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiContext; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +import java.util.function.Supplier; + +/** + * Component providing XML wrappers and such the ability to wrap meta operations that operate on the entire screen. + * This component should be permanently mounted and does not impact layouting or rendering. + */ +public class MetaComponent extends GuiComponent implements CloseEventListener { + private final Supplier beforeClose; + private final Runnable afterClose; + private final GetSetter requestClose; + + public MetaComponent() { + this(null, null, null); + } + + public MetaComponent(Supplier beforeClose, Runnable afterClose, GetSetter requestClose) { + this.beforeClose = beforeClose; + this.afterClose = afterClose; + this.requestClose = requestClose; + } + + @Override + public void setContext(GuiContext context) { + super.setContext(context); + if (requestClose != null) { + requestClose.set(() -> { + if (context != null) { + context.requestClose(); + } + }); + } + } + + @Override + public CloseAction onBeforeClose() { + return beforeClose != null ? beforeClose.get() : CloseAction.NO_OBJECTIONS_TO_CLOSE; + } + + @Override + public void onAfterClose() { + if (afterClose != null) { + afterClose.run(); + } + } + + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public void render(GuiImmediateContext context) { + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.kt deleted file mode 100644 index 384b368ff..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/MetaComponent.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.CloseEventListener -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiContext -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import java.util.function.Supplier - -/** - * Component providing XML wrappers and such the ability to wrap meta operations that operate on the entire screen. - * This component should be permanently mounted and does not impact layouting or rendering. - */ -class MetaComponent( - val beforeClose: Supplier? = null, - val afterClose: Runnable? = null, - val requestClose: GetSetter? = null -) : GuiComponent(), CloseEventListener { - override fun setContext(context: GuiContext?) { - super.setContext(context) - requestClose?.set(Runnable { - context?.requestClose() - }) - } - - override fun onBeforeClose(): CloseEventListener.CloseAction { - return beforeClose?.get() ?: CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE - } - - override fun onAfterClose() { - afterClose?.run() - } - - override fun getWidth(): Int { - return 0 - } - - override fun getHeight(): Int { - return 0 - } - - override fun render(context: GuiImmediateContext) { - } -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.java new file mode 100644 index 000000000..71e7ca88e --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.java @@ -0,0 +1,53 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; + +import java.util.function.BiFunction; +import java.util.function.Supplier; + +public class ScaleComponent extends GuiComponent { + private final GuiComponent child; + private final Supplier scaleFactor; + + public ScaleComponent(GuiComponent child, Supplier scaleFactor) { + this.child = child; + this.scaleFactor = scaleFactor; + } + + @Override + public int getWidth() { + return (int) (scaleFactor.get() * child.getWidth()); + } + + @Override + public int getHeight() { + return (int) (scaleFactor.get() * child.getHeight()); + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + return visitor.apply(child, initial); + } + + @Override + public void render(GuiImmediateContext context) { + context.getRenderContext().pushMatrix(); + float scale = scaleFactor.get(); + context.getRenderContext().scale(scale, scale); + child.render(context.scaled(scale)); + context.getRenderContext().popMatrix(); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + return child.keyboardEvent(event, context.scaled(scaleFactor.get())); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + return child.mouseEvent(mouseEvent, context.scaled(scaleFactor.get())); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.kt deleted file mode 100644 index b7da50bf4..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/ScaleComponent.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import java.util.function.BiFunction -import java.util.function.Supplier - -class ScaleComponent( - val child: GuiComponent, - val scaleFactor: Supplier, -) : GuiComponent() { - override fun getWidth(): Int { - return (scaleFactor.get() * child.width).toInt() - } - - override fun getHeight(): Int { - return (scaleFactor.get() * child.height).toInt() - } - - override fun foldChildren( - initial: T, - visitor: BiFunction - ): T { - return visitor.apply(child, initial) - } - - override fun render(context: GuiImmediateContext) { - context.renderContext.pushMatrix() - val s = scaleFactor.get() - context.renderContext.scale(s, s) - child.render(context.scaled(s)) - context.renderContext.popMatrix() - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - return child.keyboardEvent(event, context.scaled(scaleFactor.get())) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - return child.mouseEvent(mouseEvent, context.scaled(scaleFactor.get())) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.java new file mode 100644 index 000000000..c2959e0c7 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.java @@ -0,0 +1,105 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.GuiTextures; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +public class SliderComponent extends GuiComponent { + protected final GetSetter value; + protected final float minValue; + protected final float maxValue; + protected final float minStep; + private final int width; + protected boolean clicked; + + public SliderComponent(GetSetter value, float minValue, float maxValue, float minStep, int width) { + this.value = value; + this.minValue = minValue; + this.maxValue = maxValue; + this.minStep = minStep; + this.width = width; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return 16; + } + + @Override + public void render(GuiImmediateContext context) { + if (clicked) { + setValueFromContext(context); + } + float currentValue = getValueAsFloat(); + context.getRenderContext().drawTexturedRect(GuiTextures.SLIDER_ON_CAP, 0F, 0F, 4F, (float) context.getHeight()); + context.getRenderContext().drawTexturedRect(GuiTextures.SLIDER_OFF_CAP, (float) (width - 4), 0F, 4F, (float) context.getHeight()); + float coerced = Math.max(minValue, Math.min(maxValue, currentValue)); + int sliderPosition = (int) ((coerced - minValue) / (maxValue - minValue) * context.getWidth()); + if (sliderPosition > 5) { + context.getRenderContext().drawTexturedRect(GuiTextures.SLIDER_ON_SEGMENT, 4F, 0F, (float) (sliderPosition - 4), (float) context.getHeight()); + } + if (sliderPosition < context.getWidth() - 5) { + context.getRenderContext().drawTexturedRect( + GuiTextures.SLIDER_OFF_SEGMENT, + (float) sliderPosition, + 0F, + (float) (context.getWidth() - 4 - sliderPosition), + (float) context.getHeight() + ); + } + for (int i = 0; i <= 3; i++) { + int notchX = context.getWidth() * i / 4 - 1; + context.getRenderContext().drawTexturedRect( + notchX > sliderPosition ? GuiTextures.SLIDER_OFF_NOTCH : GuiTextures.SLIDER_ON_NOTCH, + (float) notchX, + (context.getHeight() - 4) / 2F, + 2F, + 4F + ); + } + context.getRenderContext().drawTexturedRect(GuiTextures.SLIDER_BUTTON, (float) (sliderPosition - 4), 0F, 8F, (float) context.getHeight()); + } + + public void setValueFromContext(GuiImmediateContext context) { + float v = context.getMouseX() * (maxValue - minValue) / context.getWidth() + minValue; + v = Math.min(v, maxValue); + v = Math.max(v, minValue); + v = Math.round(v / minStep) * minStep; + setValue(v); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + if (!context.getRenderContext().isMouseButtonDown(0)) { + clicked = false; + } + if (context.isHovered() && mouseEvent instanceof MouseEvent.Click) { + MouseEvent.Click click = (MouseEvent.Click) mouseEvent; + if (click.getMouseState() && click.getMouseButton() == 0) { + clicked = true; + } + } + if (clicked) { + setValueFromContext(context); + return true; + } + return false; + } + + protected float getValueAsFloat() { + return value.get().floatValue(); + } + + // Slider components accept any numeric getter; config-backed setters coerce the float write to the declared field type. + @SuppressWarnings("unchecked") + protected void setValue(float newValue) { + ((GetSetter) value).set(newValue); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.kt deleted file mode 100644 index 927c43fe7..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponent.kt +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.GuiTextures -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import kotlin.math.max -import kotlin.math.min - -open class SliderComponent( - val value: GetSetter, - val minValue: Float, - val maxValue: Float, - val minStep: Float, - private val width: Int, -) : GuiComponent() { - var clicked: Boolean = false - override fun getWidth(): Int { - return width - } - - override fun getHeight(): Int { - return 16 - } - - override fun render(context: GuiImmediateContext) { - if (clicked) { - setValueFromContext(context) - } - val value: Float = value.get() - context.renderContext.drawTexturedRect(GuiTextures.SLIDER_ON_CAP, 0F, 0F, 4F, context.height.toFloat()) - context.renderContext.drawTexturedRect(GuiTextures.SLIDER_OFF_CAP, (width - 4).toFloat(), 0F, 4F, context.height.toFloat()) - val sliderPosition = ((value.coerceIn(minValue..maxValue) - minValue) / (maxValue - minValue) * context.width).toInt() - if (sliderPosition > 5) { - context.renderContext.drawTexturedRect(GuiTextures.SLIDER_ON_SEGMENT, 4F, 0F, (sliderPosition - 4).toFloat(), context.height.toFloat()) - } - if (sliderPosition < context.width - 5) { - context.renderContext.drawTexturedRect( - GuiTextures.SLIDER_OFF_SEGMENT, - sliderPosition.toFloat(), - 0F, - (context.width - 4 - sliderPosition).toFloat(), - context.height.toFloat() - ) - } - for (i in 0..3) { - val notchX = context.width * i / 4 - 1 - context.renderContext.drawTexturedRect( - if (notchX > sliderPosition) GuiTextures.SLIDER_OFF_NOTCH else GuiTextures.SLIDER_ON_NOTCH, - notchX.toFloat(), (context.height - 4) / 2F, 2F, 4F - ) - } - context.renderContext.drawTexturedRect( - GuiTextures.SLIDER_BUTTON, - (sliderPosition - 4).toFloat(), 0F, 8F, context.height.toFloat() - ) - } - - open fun setValueFromContext(context: GuiImmediateContext) { - var v: Float = context.mouseX * (maxValue - minValue) / context.width + minValue - v = min(v.toDouble(), maxValue.toDouble()).toFloat() - v = max(v.toDouble(), minValue.toDouble()).toFloat() - v = Math.round(v / minStep) * minStep - value.set(v) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - if (!context.renderContext.isMouseButtonDown(0)) clicked = false - if (context.isHovered && mouseEvent is MouseEvent.Click && mouseEvent.mouseState && mouseEvent.mouseButton == 0) { - clicked = true - } - if (clicked) { - setValueFromContext(context) - return true - } - return false - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.java new file mode 100644 index 000000000..8154632f6 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.java @@ -0,0 +1,112 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +import java.util.function.BiFunction; + +public class SliderWithTextComponent extends SliderComponent { + private TextFieldComponent componentNumberInput; + + public SliderWithTextComponent(GetSetter value, float minValue, float maxValue, float minStep, int width) { + super(value, minValue, maxValue, minStep, width); + } + + @Override + public void render(GuiImmediateContext context) { + context.getRenderContext().translate(-(getWidth() / 3F), 0F); + super.render(context); + context.getRenderContext().translate(60F, -5F); + getComponentNumberInput().render(context.translated(60, -5, getComponentNumberInput().getWidth(), 18)); + } + + private boolean isHovered(GuiImmediateContext context) { + return context.getMouseX() >= -getWidth() / 3 + && context.getMouseX() < getWidth() - 13 + && context.getMouseY() >= 0 + && context.getMouseY() < context.getHeight(); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + if (!context.getRenderContext().isMouseButtonDown(0)) { + clicked = false; + } + if (isHovered(context) && mouseEvent instanceof MouseEvent.Click) { + MouseEvent.Click click = (MouseEvent.Click) mouseEvent; + if (click.getMouseState() && click.getMouseButton() == 0) { + clicked = true; + } + } + if (clicked) { + setValueFromContext(context); + return true; + } + return getComponentNumberInput().mouseEvent(mouseEvent, context.translated(45, -5, getComponentNumberInput().getWidth(), 18)); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + getComponentNumberInput().setShouldExpandToFit(true); + return getComponentNumberInput().keyboardEvent(event, context); + } + + @Override + public void setValueFromContext(GuiImmediateContext context) { + float v = (context.getMouseX() + getWidth() / 3F) * (maxValue - minValue) / context.getWidth() + minValue; + v = Math.min(v, maxValue); + v = Math.max(v, minValue); + v = Math.round(v / minStep) * minStep; + setValue(v); + } + + private TextFieldComponent getComponentNumberInput() { + if (componentNumberInput == null) { + componentNumberInput = new TextFieldComponent(new GetSetter() { + private String editingBuffer = ""; + + @Override + public String get() { + if (isInFocus()) { + return editingBuffer; + } + float num; + try { + num = getValueAsFloat(); + } catch (NumberFormatException e) { + num = 0F; + } + String stringNum = Float.toString(num); + if (stringNum.endsWith(".0")) { + stringNum = stringNum.substring(0, stringNum.length() - 2); + } + editingBuffer = stringNum; + return stringNum; + } + + @Override + public void set(String newValue) { + editingBuffer = newValue; + float num; + try { + num = Float.parseFloat(editingBuffer); + } catch (NumberFormatException e) { + num = 0F; + } + setValue(num); + editingBuffer = Float.toString(num); + } + }, 20, GetSetter.constant(true), "", IMinecraft.INSTANCE.getDefaultFontRenderer()); + } + return componentNumberInput; + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + return visitor.apply(getComponentNumberInput(), initial); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.kt deleted file mode 100644 index 5fc8991c3..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SliderWithTextComponent.kt +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import java.util.function.BiFunction -import kotlin.math.max -import kotlin.math.min - -open class SliderWithTextComponent( - value: GetSetter, - minValue: Float, - maxValue: Float, - minStep: Float, - width: Int, -) : SliderComponent(value, minValue, maxValue, minStep, width) { - - - override fun render(context: GuiImmediateContext) { - context.renderContext.translate(-(width/3).toFloat(), 0f) - super.render(context) - context.renderContext.translate(60f, -5f) - componentNumberInput.width - componentNumberInput.render(context.translated(60, -5, componentNumberInput.width, 18)) - } - - // I made this because I couldn't get isHovered to work with the translation - private fun GuiImmediateContext.isHovered(): Boolean { - return mouseX in 0 - width/3 until width - 13 && mouseY in 0 until height - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - if (!context.renderContext.isMouseButtonDown(0)) clicked = false - if (context.isHovered() && mouseEvent is MouseEvent.Click && mouseEvent.mouseState && mouseEvent.mouseButton == 0) { - clicked = true - } - if (clicked) { - setValueFromContext(context) - return true - } - return componentNumberInput.mouseEvent(mouseEvent, context.translated(45, -5, componentNumberInput.width, 18)) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - componentNumberInput.setShouldExpandToFit(true) - return componentNumberInput.keyboardEvent(event, context) - } - - override fun setValueFromContext(context: GuiImmediateContext) { - var v: Float = (context.mouseX + width/3) * (maxValue - minValue) / context.width + minValue - v = min(v.toDouble(), maxValue.toDouble()).toFloat() - v = max(v.toDouble(), minValue.toDouble()).toFloat() - v = Math.round(v / minStep) * minStep - value.set(v) - } - - private val componentNumberInput by lazy { - TextFieldComponent( - object : GetSetter { - - var editingBuffer: String = "" - - override fun get(): String { - if (isInFocus) return editingBuffer - var num: Float - try { - num = value.get() - } catch (e: NumberFormatException) { - num = 0f - } - val stringNum = num.toString().removeSuffix(".0") - return stringNum.also { editingBuffer = it } - } - - override fun set(newValue: String) { - editingBuffer = newValue - var num: Float - try { - num = editingBuffer.toFloat() - } catch (e: NumberFormatException) { - num = 0f - } - value.set(num).also { editingBuffer = num.toString() } - } - }, - 20, - GetSetter.constant(true), - "", - IMinecraft.INSTANCE.defaultFontRenderer - ) - } - - override fun foldChildren(initial: T, visitor: BiFunction): T { - return visitor.apply(componentNumberInput, initial) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.java new file mode 100644 index 000000000..101a67a04 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.java @@ -0,0 +1,30 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; + +import java.util.function.Supplier; + +public class SpacerComponent extends GuiComponent { + private final Supplier width; + private final Supplier height; + + public SpacerComponent(Supplier width, Supplier height) { + this.width = width; + this.height = height; + } + + @Override + public int getWidth() { + return width.get(); + } + + @Override + public int getHeight() { + return height.get(); + } + + @Override + public void render(GuiImmediateContext context) { + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.kt deleted file mode 100644 index 9dfc218de..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/SpacerComponent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import java.util.function.Supplier - -class SpacerComponent( - val width: Supplier, - val height: Supplier, -) : GuiComponent() { - override fun getWidth(): Int { - return width.get() - } - - override fun getHeight(): Int { - return height.get() - } - - override fun render(context: GuiImmediateContext) { - } -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.java new file mode 100644 index 000000000..e7cdeb357 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.java @@ -0,0 +1,208 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.GuiTextures; +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.NinePatches; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import juuxel.libninepatch.NinePatch; + +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; + +public class TabComponent extends GuiComponent { + public static final class Tab { + private final GuiComponent header; + private final GuiComponent body; + + public Tab(GuiComponent header, GuiComponent body) { + this.header = header; + this.body = body; + } + + public GuiComponent getHeader() { + return header; + } + + public GuiComponent getBody() { + return body; + } + + public GuiComponent component1() { return header; } + public GuiComponent component2() { return body; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Tab)) return false; + Tab tab = (Tab) o; + return Objects.equals(header, tab.header) && Objects.equals(body, tab.body); + } + + @Override + public int hashCode() { + return Objects.hash(header, body); + } + + @Override + public String toString() { + return "Tab(header=" + header + ", body=" + body + ")"; + } + } + + private final List tabs; + private final GetSetter selectedTabIndex; + public final NinePatch panelStyle = NinePatches.createVanillaPanel(); + public final NinePatch tabSelectedHeaderBackground = NinePatch.builder(GuiTextures.VANILLA_TAB_SELECTED) + .cornerSize(4) + .cornerUv(4 / 16F) + .mode(NinePatch.Mode.STRETCHING) + .build(); + public final NinePatch tabUnselectedHeaderBackground = NinePatch.builder(GuiTextures.VANILLA_TAB_UNSELECTED) + .cornerSize(4) + .cornerUv(4 / 16F) + .mode(NinePatch.Mode.STRETCHING) + .build(); + + public final int headerPadding = 4; + public final int headerSpacing = 4; + public final int bodyPadding = 4; + public final int initialHeaderOffset = bodyPadding + 4; + public final int headerInset = 4; + + public TabComponent(List tabs, GetSetter selectedTabIndex) { + if (tabs.isEmpty()) { + throw new IllegalArgumentException("tabs must not be empty"); + } + this.tabs = tabs; + this.selectedTabIndex = selectedTabIndex; + } + + public List getTabs() { + return tabs; + } + + public GetSetter getSelectedTabIndex() { + return selectedTabIndex; + } + + @Override + public int getWidth() { + int maxBodyWidth = 0; + int totalHeaderWidth = initialHeaderOffset; + for (Tab tab : tabs) { + maxBodyWidth = Math.max(maxBodyWidth, tab.body.getWidth()); + totalHeaderWidth += tab.header.getWidth() + headerSpacing + headerPadding * 2; + } + return Math.max(maxBodyWidth + 2 * bodyPadding, totalHeaderWidth); + } + + @Override + public int getHeight() { + int maxBodyHeight = 0; + int maxHeaderHeight = 0; + for (Tab tab : tabs) { + maxBodyHeight = Math.max(maxBodyHeight, tab.body.getHeight()); + maxHeaderHeight = Math.max(maxHeaderHeight, tab.header.getHeight()); + } + return maxBodyHeight + maxHeaderHeight + bodyPadding * 2 + headerPadding; + } + + @Override + public T foldChildren(T initial, BiFunction visitor) { + T acc = initial; + for (Tab tab : tabs) { + acc = visitor.apply(tab.header, visitor.apply(tab.body, acc)); + } + return acc; + } + + @Override + public void render(GuiImmediateContext context) { + int headerHeight = maxHeaderHeight(); + context.getRenderContext().drawNinePatch(panelStyle, 0F, (float) (headerHeight + headerPadding), context.getWidth(), context.getHeight() - headerHeight - headerPadding); + + Tab selectedTab = null; + int headerOffset = initialHeaderOffset; + for (int index = 0; index < tabs.size(); index++) { + Tab tab = tabs.get(index); + boolean selected = index == selectedTabIndex.get(); + NinePatch background; + if (selected) { + selectedTab = tab; + background = tabSelectedHeaderBackground; + } else { + background = tabUnselectedHeaderBackground; + } + context.getRenderContext().drawNinePatch(background, (float) headerOffset, 0F, tab.header.getWidth() + 2 * headerPadding, headerHeight + headerPadding + headerInset); + GuiImmediateContext child = context.translated(headerOffset + headerPadding, headerPadding, tab.header.getWidth(), headerHeight); + context.getRenderContext().pushMatrix(); + context.getRenderContext().translate((float) (headerOffset + headerPadding), (float) headerPadding); + tab.header.render(child); + context.getRenderContext().popMatrix(); + headerOffset += tab.header.getWidth() + 2 * headerPadding + headerSpacing; + } + + if (selectedTab == null) { + return; + } + + GuiImmediateContext child = context.translated(bodyPadding, headerHeight + bodyPadding, context.getWidth() - bodyPadding * 2, context.getHeight() - headerHeight - bodyPadding); + context.getRenderContext().pushMatrix(); + context.getRenderContext().translate((float) bodyPadding, (float) (headerHeight + bodyPadding)); + selectedTab.body.render(child); + context.getRenderContext().popMatrix(); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + int headerHeight = maxHeaderHeight(); + Tab selectedTab = null; + int headerOffset = initialHeaderOffset; + for (int index = 0; index < tabs.size(); index++) { + Tab tab = tabs.get(index); + if (index == selectedTabIndex.get()) { + selectedTab = tab; + } + GuiImmediateContext child = context.translated(headerOffset + headerPadding, headerPadding, tab.header.getWidth(), headerHeight); + if (child.isHovered() && mouseEvent instanceof MouseEvent.Click && ((MouseEvent.Click) mouseEvent).getMouseState()) { + if (selectedTabIndex.get() != index) { + getContext().setFocusedElement(null); + } + selectedTabIndex.set(index); + return true; + } + headerOffset += tab.header.getWidth() + 2 * headerPadding + headerSpacing; + } + + if (selectedTab == null) return false; + GuiImmediateContext child = context.translated(bodyPadding, headerHeight + bodyPadding, context.getWidth() - bodyPadding * 2, context.getHeight() - headerHeight - bodyPadding); + return selectedTab.body.mouseEvent(mouseEvent, child); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + int headerHeight = maxHeaderHeight(); + Tab selectedTab = null; + for (int index = 0; index < tabs.size(); index++) { + if (index == selectedTabIndex.get()) { + selectedTab = tabs.get(index); + } + } + if (selectedTab == null) return false; + GuiImmediateContext child = context.translated(bodyPadding, headerHeight + bodyPadding, context.getWidth() - bodyPadding * 2, context.getHeight() - headerHeight - bodyPadding); + return selectedTab.body.keyboardEvent(event, child); + } + + private int maxHeaderHeight() { + int headerHeight = 0; + for (Tab tab : tabs) { + headerHeight = Math.max(headerHeight, tab.header.getHeight()); + } + return headerHeight; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.kt deleted file mode 100644 index b4ac67358..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TabComponent.kt +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.GuiTextures -import io.github.notenoughupdates.moulconfig.common.NinePatches -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import juuxel.libninepatch.NinePatch -import java.util.function.BiFunction - -data class TabComponent( - val tabs: List, - val selectedTabIndex: GetSetter -) : GuiComponent() { - data class Tab( - val header: GuiComponent, - val body: GuiComponent, - ) - - val panelStyle = NinePatches.createVanillaPanel() - - val tabSelectedHeaderBackground = NinePatch.builder(GuiTextures.VANILLA_TAB_SELECTED) - .cornerSize(4) - .cornerUv(4 / 16F) - .mode(NinePatch.Mode.STRETCHING) - .build() - val tabUnselectedHeaderBackground = NinePatch.builder(GuiTextures.VANILLA_TAB_UNSELECTED) - .cornerSize(4) - .cornerUv(4 / 16F) - .mode(NinePatch.Mode.STRETCHING) - .build() - - val headerPadding = 4 // Top left and right padding of the tab headers - val headerSpacing = 4 // Spacing between two header tabs - val bodyPadding = 4 // Bottom left and right padding of the main content panel - val initialHeaderOffset = bodyPadding + 4 // Spacing from the left to the first header tab - val headerInset = 4 // How far the tab header should be inset and override the panel - - init { - require(tabs.isNotEmpty()) - } - - override fun getWidth(): Int { - return maxOf(tabs.maxOf { it.body.width } + 2 * bodyPadding, - initialHeaderOffset + tabs.sumOf { it.header.width + headerSpacing + headerPadding * 2 }) - } - - override fun getHeight(): Int { - return tabs.maxOf { it.body.height } + tabs.maxOf { it.header.height } + bodyPadding * 2 + headerPadding - } - - override fun foldChildren(initial: T, visitor: BiFunction): T { - return tabs.fold(initial) { acc, tab -> visitor.apply(tab.header, visitor.apply(tab.body, acc)) } - } - - override fun render(context: GuiImmediateContext) { - val headerHeight = tabs.maxOf { it.header.height } - - context.renderContext.drawNinePatch( - panelStyle, - 0.toFloat(), headerHeight.toFloat() + headerPadding, - context.width, context.height - headerHeight - headerPadding - ) - - var selectedTab: Tab? = null - var headerOffset = initialHeaderOffset - for ((index, tab) in tabs.withIndex()) { - val isSelected = index == selectedTabIndex.get() - val background = if (isSelected) { - selectedTab = tab - tabSelectedHeaderBackground - } else { - tabUnselectedHeaderBackground - } - context.renderContext.drawNinePatch( - background, - headerOffset.toFloat(), 0f, - tab.header.width + 2 * headerPadding, headerHeight + headerPadding + headerInset - ) - val child = context.translated( - headerOffset + headerPadding, headerPadding, - tab.header.width, headerHeight) - context.renderContext.pushMatrix() - context.renderContext.translate(headerOffset + headerPadding + 0F, headerPadding + 0F) - tab.header.render(child) - context.renderContext.popMatrix() - headerOffset += tab.header.width + 2 * headerPadding + headerSpacing - } - - selectedTab ?: return - - val child = context.translated(bodyPadding, headerHeight + bodyPadding, context.width - bodyPadding * 2, context.height - headerHeight - bodyPadding) - context.renderContext.pushMatrix() - context.renderContext.translate(bodyPadding + 0F, headerHeight + bodyPadding + 0F) - selectedTab.body.render(child) - context.renderContext.popMatrix() - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - val headerHeight = tabs.maxOf { it.header.height } - - var selectedTab: Tab? = null - var headerOffset = initialHeaderOffset - for ((index, tab) in tabs.withIndex()) { - val isSelected = index == selectedTabIndex.get() - if (isSelected) { - selectedTab = tab - } - val child = context.translated( - headerOffset + headerPadding, headerPadding, - tab.header.width, headerHeight) - if (child.isHovered && mouseEvent is MouseEvent.Click && mouseEvent.mouseState) { - if (selectedTabIndex.get() != index) { - this.context.setFocusedElement(null) - } - selectedTabIndex.set(index) - return true - } - headerOffset += tab.header.width + 2 * headerPadding + headerSpacing - } - - selectedTab ?: return false - - val child = context.translated(bodyPadding, headerHeight + bodyPadding, context.width - bodyPadding * 2, context.height - headerHeight - bodyPadding) - return selectedTab.body.mouseEvent(mouseEvent, child) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - val headerHeight = tabs.maxOf { it.header.height } - - var selectedTab: Tab? = null - for ((index, tab) in tabs.withIndex()) { - val isSelected = index == selectedTabIndex.get() - if (isSelected) { - selectedTab = tab - } - } - - selectedTab ?: return false - - val child = context.translated(bodyPadding, headerHeight + bodyPadding, context.width - bodyPadding * 2, context.height - headerHeight - bodyPadding) - return selectedTab.body.keyboardEvent(event, child) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.java new file mode 100644 index 000000000..27b4ab105 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.java @@ -0,0 +1,355 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.common.KeyboardConstants; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent; +import io.github.notenoughupdates.moulconfig.gui.MouseEvent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class TextFieldComponent extends GuiComponent { + private static final int TEXT_PADDING_X = 4; + private static final int BACKGROUND_COLOR = 0xFF000000; + private static final int BORDER_COLOR_SELECTED = 0xFF00FF00; + private static final int BORDER_COLOR_UNSELECTED = 0xFFFFFFFF; + private static final int ENABLED_COLOR = 0xFFE0E0E0; + private static final int SUGGESTION_COLOR = 0xFF808080; + private static final int DISABLED_COLOR = 0xFF707070; + private static final int CURSOR_COLOR = 0xFFD0D0D0; + private static final int TEXT_PADDING_Y = 2; + + protected final GetSetter text; + private final int preferredWidth; + protected final Supplier editable; + protected final String suggestion; + protected final IFontRenderer font; + protected final Set forbiddenChars; + + private int cursor; + private int selection = -1; + private int scrollOffset; + private String visibleText; + private boolean shouldExpandToFit; + private boolean initializedCursor; + + public TextFieldComponent(GetSetter text, int preferredWidth) { + this(text, preferredWidth, GetSetter.constant(true), "", IMinecraft.INSTANCE.getDefaultFontRenderer(), singletonCharSet('\u00a7')); + } + + public TextFieldComponent(GetSetter text, int preferredWidth, Supplier editable, String suggestion) { + this(text, preferredWidth, editable, suggestion, IMinecraft.INSTANCE.getDefaultFontRenderer(), singletonCharSet('\u00a7')); + } + + public TextFieldComponent(GetSetter text, int preferredWidth, Supplier editable, String suggestion, IFontRenderer font) { + this(text, preferredWidth, editable, suggestion, font, singletonCharSet('\u00a7')); + } + + public TextFieldComponent(GetSetter text, int preferredWidth, Supplier editable, String suggestion, IFontRenderer font, Set forbiddenChars) { + this.text = text; + this.preferredWidth = preferredWidth; + this.editable = editable; + this.suggestion = suggestion; + this.font = font; + this.forbiddenChars = forbiddenChars; + } + + private static Set singletonCharSet(char c) { + return Collections.singleton(c); + } + + public GetSetter getText() { + return text; + } + + @Override + public int getWidth() { + if (isFocused() && shouldExpandToFit) { + return Math.max(preferredWidth, font.getStringWidth(StructuredText.of(text.get())) + 10); + } + return preferredWidth; + } + + @Override + public int getHeight() { + return 14; + } + + public void scrollCursorIntoView(int width) { + validateCursor(); + if (scrollOffset > cursor) { + scrollOffset = cursor; + } + if (scrollOffset < cursor + && font.trimStringToWidth(safeSubString(text.get(), scrollOffset), width - TEXT_PADDING_X * 2).length() + scrollOffset < cursor) { + scrollOffset = cursor; + } + checkScrollOffset(width); + } + + public void checkScrollOffset(int width) { + String value = text.get(); + int rightMostScrollOffset = value.length() - font.trimStringToWidth(value, width - TEXT_PADDING_X * 2, true).length(); + scrollOffset = Math.max(0, Math.min(rightMostScrollOffset, scrollOffset)); + } + + public void updateVisibleText(int width) { + visibleText = font.trimStringToWidth(safeSubString(text.get(), scrollOffset), width - TEXT_PADDING_X * 2); + } + + @Override + public void render(GuiImmediateContext context) { + validateCursor(); + checkScrollOffset(context.getWidth()); + updateVisibleText(context.getWidth()); + renderBox(context); + renderText(context, visibleText); + if (text.get().isEmpty() && !isFocused()) { + context.getRenderContext().drawString( + font, + StructuredText.of(suggestion), + TEXT_PADDING_X, + context.getHeight() / 2 - font.getHeight() / 2, + SUGGESTION_COLOR, + false + ); + } + if (isFocused()) { + renderCursor(context); + } + renderSelection(context); + } + + public void validateCursor() { + cursor = Math.max(0, Math.min(text.get().length(), cursor)); + } + + private void renderSelection(GuiImmediateContext context) { + if (selection == cursor || selection == -1) return; + int left = Math.min(cursor, selection); + int right = Math.max(cursor, selection); + if (right < scrollOffset || left > scrollOffset + visibleText.length()) return; + int normalizedLeft = Math.max(scrollOffset, left) - scrollOffset; + int normalizedRight = Math.min(scrollOffset + visibleText.length(), right) - scrollOffset; + int leftPos = font.getStringWidth(safeSubString(visibleText, 0, normalizedLeft)); + int rightPos = leftPos + font.getStringWidth(safeSubString(visibleText, normalizedLeft, normalizedRight)); + context.getRenderContext().invertedRect( + (float) (TEXT_PADDING_X + leftPos), + (float) TEXT_PADDING_Y, + (float) (TEXT_PADDING_X + rightPos), + (float) (context.getHeight() - TEXT_PADDING_Y), + 0xFF0000FF + ); + } + + private void renderCursor(GuiImmediateContext context) { + if (System.currentTimeMillis() / 1000 % 2 == 0) return; + if (cursor < scrollOffset) return; + if (cursor > scrollOffset + visibleText.length()) return; + int cursorOffset = font.getStringWidth(safeSubString(visibleText, 0, cursor - scrollOffset)); + context.getRenderContext().drawColoredRect( + (float) (TEXT_PADDING_X + cursorOffset), + (float) TEXT_PADDING_Y, + (float) (TEXT_PADDING_X + cursorOffset + 1), + (float) (context.getHeight() - TEXT_PADDING_Y), + CURSOR_COLOR + ); + } + + private void renderText(GuiImmediateContext context, String visibleText) { + int textColor = editable.get() ? ENABLED_COLOR : DISABLED_COLOR; + context.getRenderContext().drawString( + font, + StructuredText.of(visibleText), + TEXT_PADDING_X, + context.getHeight() / 2 - font.getHeight() / 2, + textColor, + true + ); + } + + private void renderBox(GuiImmediateContext context) { + int borderColor = isFocused() ? BORDER_COLOR_SELECTED : BORDER_COLOR_UNSELECTED; + context.getRenderContext().drawColoredRect(0F, 0F, (float) context.getWidth(), (float) context.getHeight(), borderColor); + context.getRenderContext().drawColoredRect(1F, 1F, (float) (context.getWidth() - 1), (float) (context.getHeight() - 1), BACKGROUND_COLOR); + } + + @Override + public boolean keyboardEvent(KeyboardEvent event, GuiImmediateContext context) { + if (!editable.get()) return false; + if (!isFocused()) return false; + if (event instanceof KeyboardEvent.KeyPressed) { + KeyboardEvent.KeyPressed keyPressed = (KeyboardEvent.KeyPressed) event; + if (!keyPressed.getPressed()) return false; + int keycode = keyPressed.getKeycode(); + if (keycode == KeyboardConstants.INSTANCE.getLeft()) { + onDirectionalKey(context, -1); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getRight()) { + onDirectionalKey(context, 1); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getHome() || keycode == KeyboardConstants.INSTANCE.getUp()) { + if (context.getRenderContext().isShiftDown()) { + if (selection == -1) selection = cursor; + } else { + selection = -1; + } + cursor = 0; + scrollCursorIntoView(context.getWidth()); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getDown() || keycode == KeyboardConstants.INSTANCE.getEnd()) { + if (context.getRenderContext().isShiftDown()) { + if (selection == -1) selection = cursor; + } else { + selection = -1; + } + cursor = text.get().length(); + scrollCursorIntoView(context.getWidth()); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getBackSpace()) { + if (selection == -1) selection = skipCharacters(context.getRenderContext().isLogicalCtrlDown(), -1); + writeText("", context.getWidth()); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getDelete()) { + if (selection == -1) selection = skipCharacters(context.getRenderContext().isLogicalCtrlDown(), 1); + writeText("", context.getWidth()); + return true; + } else if (keycode == KeyboardConstants.INSTANCE.getKeyC()) { + if (context.getRenderContext().isLogicalCtrlDown()) { + IMinecraft.INSTANCE.copyToClipboard(getSelection()); + return true; + } + return false; + } else if (keycode == KeyboardConstants.INSTANCE.getKeyX()) { + if (context.getRenderContext().isLogicalCtrlDown()) { + IMinecraft.INSTANCE.copyToClipboard(getSelection()); + writeText("", context.getWidth()); + return true; + } + return false; + } else if (keycode == KeyboardConstants.INSTANCE.getKeyV()) { + if (context.getRenderContext().isLogicalCtrlDown()) { + writeText(IMinecraft.INSTANCE.copyFromClipboard(), context.getWidth()); + return true; + } + return false; + } else if (keycode == KeyboardConstants.INSTANCE.getKeyA()) { + if (context.getRenderContext().isLogicalCtrlDown()) { + cursor = text.get().length(); + selection = 0; + scrollCursorIntoView(context.getWidth()); + return true; + } + return false; + } + return false; + } else if (event instanceof KeyboardEvent.CharTyped) { + char c = ((KeyboardEvent.CharTyped) event).getChar(); + if (c < ' ' || c == 127) return false; + if (forbiddenChars.contains(c)) return true; + writeText(Character.toString(c), context.getWidth()); + return true; + } + return false; + } + + private String getSelection() { + if (selection == -1) return ""; + int left = Math.min(cursor, selection); + int right = Math.max(cursor, selection); + return safeSubString(text.get(), left, right); + } + + @Override + public boolean mouseEvent(MouseEvent mouseEvent, GuiImmediateContext context) { + super.mouseEvent(mouseEvent, context); + checkScrollOffset(context.getWidth()); + updateVisibleText(context.getWidth()); + if (mouseEvent instanceof MouseEvent.Click && ((MouseEvent.Click) mouseEvent).getMouseState()) { + if (context.isHovered()) { + requestFocus(); + if (!initializedCursor) { + initializedCursor = true; + cursor = Integer.MAX_VALUE; + validateCursor(); + scrollCursorIntoView(context.getWidth()); + } + return true; + } else { + setFocus(false); + } + } + return false; + } + + private String safeSubString(String str, int startIndex) { + return str.substring(Math.min(startIndex, str.length())); + } + + private String safeSubString(String str, int startIndex, int endIndex) { + return str.substring(Math.min(startIndex, str.length()), Math.min(Math.max(startIndex, endIndex), str.length())); + } + + public void writeText(String s, int width) { + StringBuilder filtered = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!forbiddenChars.contains(c)) { + filtered.append(c); + } + } + String filteredString = filtered.toString(); + if (filteredString.isEmpty() && !s.isEmpty()) return; + + String current = text.get(); + if (selection == -1) { + text.set(safeSubString(current, 0, cursor) + filteredString + safeSubString(current, cursor)); + cursor += filteredString.length(); + } else { + int left = Math.min(cursor, selection); + int right = Math.max(cursor, selection); + text.set(safeSubString(current, 0, left) + filteredString + safeSubString(current, right)); + cursor = left + filteredString.length(); + selection = -1; + } + scrollCursorIntoView(width); + } + + public void onDirectionalKey(GuiImmediateContext context, int i) { + if (context.getRenderContext().isShiftDown()) { + if (selection == -1) selection = cursor; + cursor = skipCharacters(context.getRenderContext().isLogicalCtrlDown(), i); + } else { + if (selection != -1) { + cursor = i < 0 ? Math.min(cursor, selection) : Math.max(cursor, selection); + selection = -1; + } else { + cursor = skipCharacters(context.getRenderContext().isLogicalCtrlDown(), i); + } + } + scrollCursorIntoView(context.getWidth()); + } + + private int skipCharacters(boolean skipWords, int i) { + if (i != -1 && i != 1) return cursor; + int position = cursor; + while (true) { + position += i; + if (position < 0) return 0; + if (position > text.get().length()) return text.get().length(); + if (!skipWords) return position; + if (position < text.get().length() && Character.isWhitespace(text.get().charAt(position))) return position; + } + } + + public void setShouldExpandToFit(boolean shouldExpandToFit) { + this.shouldExpandToFit = shouldExpandToFit; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.kt deleted file mode 100644 index 1dba0d1eb..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/TextFieldComponent.kt +++ /dev/null @@ -1,358 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.common.IFontRenderer -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import io.github.notenoughupdates.moulconfig.common.KeyboardConstants -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext -import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent -import io.github.notenoughupdates.moulconfig.gui.MouseEvent.Click -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import java.util.function.Supplier -import kotlin.math.max -import kotlin.math.min - -open class TextFieldComponent( - val text: GetSetter, - private val preferredWidth: Int, - val editable: Supplier = GetSetter.constant(true), - val suggestion: String = "", - val font: IFontRenderer = IMinecraft.INSTANCE.defaultFontRenderer, - val forbiddenChars: Set = setOf('§') -) : GuiComponent() { - private var cursor = 0 - private var selection = -1 - private var scrollOffset = 0 - private var visibleText: String? = null - private var shouldExpandToFit = false - private var initializedCursor = false - override fun getWidth(): Int { - if (isFocused && shouldExpandToFit) return max(preferredWidth, font.getStringWidth(StructuredText.of(text.get())) + 10) - return preferredWidth - } - - override fun getHeight(): Int { - return 14 - } - - open fun scrollCursorIntoView(width: Int) { - validateCursor() - if (scrollOffset > cursor) scrollOffset = cursor - if (scrollOffset < cursor && - font.trimStringToWidth( - safeSubString(text.get(), scrollOffset), - width - TEXT_PADDING_X * 2 - ).length + scrollOffset < cursor - ) { - scrollOffset = cursor - } - checkScrollOffset(width) - } - - open fun checkScrollOffset(width: Int) { - val text = text.get() - val rightMostScrollOffset = text.length - font.trimStringToWidth(text, width - TEXT_PADDING_X * 2, true).length - scrollOffset = max(0, min(rightMostScrollOffset, scrollOffset)) - } - - fun updateVisibleText(width: Int) { - visibleText = - font.trimStringToWidth(safeSubString(text.get(), scrollOffset), width - TEXT_PADDING_X * 2) - - } - - override fun render(context: GuiImmediateContext) { - validateCursor() - checkScrollOffset(context.width) - updateVisibleText(context.width) - renderBox(context) - renderText(context, visibleText!!) - if (text.get().isEmpty() && !isFocused) { - context.renderContext.drawString( - font, - StructuredText.of(suggestion), - TEXT_PADDING_X, - context.height / 2 - font.height / 2, - SUGGESTION_COLOR, - false - ) - } - if (isFocused) { - renderCursor(context) - } - renderSelection(context) - } - - open fun validateCursor() { - cursor = max(0, min(text.get().length, cursor)) - } - - private fun renderSelection(context: GuiImmediateContext) { - if (selection == cursor || selection == -1) return - val left = min(cursor, selection) - val right = max(cursor, selection) - if (right < scrollOffset || left > scrollOffset + visibleText!!.length) return - val normalizedLeft = max(scrollOffset, left) - scrollOffset - val normalizedRight = - min(scrollOffset + visibleText!!.length, right) - scrollOffset - val leftPos = font.getStringWidth(safeSubString(visibleText!!, 0, normalizedLeft)) - val rightPos = leftPos + font.getStringWidth(safeSubString(visibleText!!, normalizedLeft, normalizedRight)) - context.renderContext.invertedRect( - (TEXT_PADDING_X + leftPos).toFloat(), - TEXT_PADDING_Y.toFloat(), - (TEXT_PADDING_X + rightPos).toFloat(), - (context.height - TEXT_PADDING_Y).toFloat(), - 0xFF0000FF.toInt(), - ) - } - - private fun renderCursor(context: GuiImmediateContext) { - if (System.currentTimeMillis() / 1000 % 2 == 0L) { - return - } - if (cursor < scrollOffset) return - if (cursor > scrollOffset + visibleText!!.length) return - val cursorOffset = font.getStringWidth(safeSubString(visibleText!!, 0, cursor - scrollOffset)) - context.renderContext.drawColoredRect( - (TEXT_PADDING_X + cursorOffset).toFloat(), - TEXT_PADDING_Y.toFloat(), - (TEXT_PADDING_X + cursorOffset + 1).toFloat(), - (context.height - TEXT_PADDING_Y).toFloat(), - CURSOR_COLOR - ) - } - - private fun renderText(context: GuiImmediateContext, visibleText: String) { - val textColor = if (editable.get()) ENABLED_COLOR else DISABLED_COLOR - context.renderContext.drawString( - font, StructuredText.of(visibleText), TEXT_PADDING_X, - context.height / 2 - font.height / 2, textColor, true - ) - } - - private fun renderBox(context: GuiImmediateContext) { - val borderColor = if (isFocused) BORDER_COLOR_SELECTED else BORDER_COLOR_UNSELECTED - context.renderContext.drawColoredRect(0f, 0f, context.width.toFloat(), context.height.toFloat(), borderColor) - context.renderContext.drawColoredRect( - 1f, - 1f, - (context.width - 1).toFloat(), - (context.height - 1).toFloat(), - BACKGROUND_COLOR - ) - } - - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - if (!editable.get()) return false - if (!isFocused) return false - if (event is KeyboardEvent.KeyPressed && event.pressed) { - return when (event.keycode) { - KeyboardConstants.left -> { - onDirectionalKey(context, -1) - return true - } - - KeyboardConstants.right -> { - onDirectionalKey(context, 1) - return true - } - - KeyboardConstants.home, KeyboardConstants.up -> { - if (context.renderContext.isShiftDown) { - if (selection == -1) selection = cursor - } else { - selection = -1 - } - cursor = 0 - scrollCursorIntoView(context.width) - return true - } - - KeyboardConstants.down, KeyboardConstants.end -> { - if (context.renderContext.isShiftDown) { - if (selection == -1) selection = cursor - } else { - selection = -1 - } - cursor = text.get().length - scrollCursorIntoView(context.width) - return true - } - - KeyboardConstants.backSpace -> { - if (selection == -1) selection = skipCharacters(context.renderContext.isLogicalCtrlDown, -1) - writeText("", context.width) - return true - } - - KeyboardConstants.delete -> { - if (selection == -1) selection = skipCharacters(context.renderContext.isLogicalCtrlDown, 1) - writeText("", context.width) - return true - } - - KeyboardConstants.keyC -> if (context.renderContext.isLogicalCtrlDown) { - IMinecraft.INSTANCE.copyToClipboard( - getSelection() - ) - return true - } else { - return false - } - - KeyboardConstants.keyX -> if (context.renderContext.isLogicalCtrlDown) { - IMinecraft.INSTANCE.copyToClipboard( - getSelection() - ) - writeText("", context.width) - return true - } else { - return false - } - - KeyboardConstants.keyV -> if (context.renderContext.isLogicalCtrlDown) { - writeText(IMinecraft.INSTANCE.copyFromClipboard(), context.width) - return true - } else { - return false - } - - KeyboardConstants.keyA -> if (context.renderContext.isLogicalCtrlDown) { - cursor = text.get().length - selection = 0 - scrollCursorIntoView(context.width) - return true - } else { - return false - } - - else -> return false - } - } else if (event is KeyboardEvent.CharTyped) { - val it = event.char - - if (it < ' ' || it.code == 127) return false - - // consume event, so we don't write into the searchbar the moment we type a forbidden char - if (forbiddenChars.contains(it)) return true - - writeText(it + "", context.width) - return true - } else { - return false - } - } - - private fun getSelection(): String { - if (selection == -1) return "" - val l = min(cursor, selection) - val r = max(cursor, selection) - return safeSubString(text.get(), l, r) - } - - override fun mouseEvent(mouseEvent: MouseEvent, context: GuiImmediateContext): Boolean { - super.mouseEvent(mouseEvent, context) - checkScrollOffset(context.width) - updateVisibleText(context.width) - if (mouseEvent is Click && mouseEvent.mouseState) { - if (context.isHovered) { - requestFocus() - - //Initializes the position of the cursor to the end of the text in the box when its first clicked on - if (!initializedCursor) { - initializedCursor = true - //The validate method clamps it to the end of the text for us - cursor = Int.MAX_VALUE - validateCursor() - scrollCursorIntoView(context.width) - } - - return true - } else { - setFocus(false) - } - } - return false - } - - private fun safeSubString(str: String, startIndex: Int): String { - return str.substring(min(startIndex, str.length)) - } - - private fun safeSubString(str: String, startIndex: Int, endIndex: Int): String { - return str.substring( - min(startIndex, str.length), - min(max(startIndex, endIndex), str.length) - ) - } - - fun writeText(s: String, width: Int) { - val filteredString = s.filter { it !in forbiddenChars } - - // we cancel if and only if the user tried to write / insert text, but everything got filtered out - // e.g. backspace delivers an empty string, thus should not be canceled - // -> only checking, if filteredString is empty is not sufficient (backspace = empty -> filtered = empty) - if (filteredString.isEmpty() && s.isNotEmpty()) return - - val t = text.get() - if (selection == -1) { - text.set(safeSubString(t, 0, cursor) + filteredString + safeSubString(t, cursor)) - cursor += filteredString.length - } else { - val l = min(cursor, selection) - val r = max(cursor, selection) - text.set(safeSubString(t, 0, l) + filteredString + safeSubString(t, r)) - cursor = l + filteredString.length - selection = -1 - } - scrollCursorIntoView(width) - } - - open fun onDirectionalKey(context: GuiImmediateContext, i: Int) { - if (context.renderContext.isShiftDown) { - if (selection == -1) selection = cursor - cursor = skipCharacters(context.renderContext.isLogicalCtrlDown, i) - } else { - if (selection != -1) { - cursor = if (i < 0) - min(cursor, selection) - else - max(cursor, selection) - selection = -1 - } else { - cursor = skipCharacters(context.renderContext.isLogicalCtrlDown, i) - } - } - scrollCursorIntoView(context.width) - } - - private fun skipCharacters(skipWords: Boolean, i: Int): Int { - if (i != -1 && i != 1) return cursor - var position = cursor - while (true) { - position += i - if (position < 0) return 0 - if (position > text.get().length) return text.get().length - if (!skipWords) return position - if (position < text.get().length && Character.isWhitespace(text.get()[position])) return position - } - } - - fun setShouldExpandToFit(new: Boolean) { - shouldExpandToFit = new - } - - companion object { - private const val TEXT_PADDING_X = 4 - private const val BACKGROUND_COLOR = -0x1000000 - private const val BORDER_COLOR_SELECTED = 0xFF00FF00.toInt() - private const val BORDER_COLOR_UNSELECTED = 0xFFFFFFFF.toInt() - private const val ENABLED_COLOR = -0x1f1f20 - private const val SUGGESTION_COLOR = -0x7f7f80 - private const val DISABLED_COLOR = -0x8f8f90 - private const val CURSOR_COLOR = -0x2f2f30 - private const val TEXT_PADDING_Y = 2 - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.java new file mode 100644 index 000000000..3f3b6d8ef --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.java @@ -0,0 +1,11 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; + +import java.util.function.Supplier; + +public class WhenComponent extends IndirectComponent { + public WhenComponent(Supplier condition, Supplier ifTrue, Supplier ifFalse) { + super(() -> condition.get() ? ifTrue.get() : ifFalse.get()); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.kt deleted file mode 100644 index c2ad38784..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/component/WhenComponent.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.notenoughupdates.moulconfig.gui.component - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import java.util.function.Supplier - -class WhenComponent( - val condition: Supplier, - val ifTrue: Supplier, - val ifFalse: Supplier, -) : IndirectComponent( - Supplier { - if (condition.get()) - ifTrue.get() - else - ifFalse.get() - } -) \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorButton.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorButton.java index 62125b920..07d19f1cc 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorButton.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorButton.java @@ -30,18 +30,14 @@ import io.github.notenoughupdates.moulconfig.internal.TypeUtils; import io.github.notenoughupdates.moulconfig.internal.Warnings; import io.github.notenoughupdates.moulconfig.processor.ProcessedOption; -import kotlin.Unit; -import kotlin.jvm.functions.Function0; -import kotlin.reflect.KFunction; import lombok.Getter; import lombok.val; import lombok.var; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Locale; -import java.util.Objects; public class GuiOptionEditorButton extends ComponentEditor { private final int runnableId; @@ -53,8 +49,8 @@ enum DispatchStyle { KRUNNABLE() { @Override void dispatch(GuiOptionEditorButton $this) { - var v = ((Function0) $this.option.get()).invoke(); - if (!Objects.equals(v, Unit.INSTANCE)) + Object v = invokeKotlinFunction0($this.option.get()); + if (!isKotlinUnit(v)) Warnings.warn("KRunnable dispatch of button " + $this.getDebugDeclarationLocation() + " returned non unit value " + v); } }, @@ -91,7 +87,7 @@ public GuiOptionEditorButton( Type type = option.getType(); if (TypeUtils.doesAExtendB(type, Runnable.class)) { dispatchStyle = DispatchStyle.RUNNABLE; - } else if (TypeUtils.doesAExtendB(type, Function0.class)) { + } else if (isKotlinFunction0(type)) { dispatchStyle = DispatchStyle.KRUNNABLE; } else { dispatchStyle = DispatchStyle.BY_ID; @@ -146,6 +142,46 @@ public void onClick() { dispatchStyle.dispatch(this); } + private static boolean isKotlinFunction0(Type type) { + try { + return hasInterface(TypeUtils.resolveRawType(type), kotlinName("jvm", "functions", "Function0")); + } catch (RuntimeException ignored) { + return false; + } + } + + private static boolean hasInterface(Class type, String interfaceName) { + if (type == null) return false; + for (Class iface : type.getInterfaces()) { + if (iface.getName().equals(interfaceName) || hasInterface(iface, interfaceName)) { + return true; + } + } + return hasInterface(type.getSuperclass(), interfaceName); + } + + private static Object invokeKotlinFunction0(Object function) { + try { + Method invoke = function.getClass().getMethod("invoke"); + return invoke.invoke(function); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to invoke Kotlin Function0 button " + function, e); + } + } + + private static boolean isKotlinUnit(Object value) { + return value != null && value.getClass().getName().equals(kotlinName("Unit")); + } + + private static String kotlinName(String... parts) { + StringBuilder builder = new StringBuilder(); + builder.append(new String(new char[]{'k', 'o', 't', 'l', 'i', 'n'})); + for (String part : parts) { + builder.append('.').append(part); + } + return builder.toString(); + } + @Override public boolean fulfillsSearch(String word) { return super.fulfillsSearch(word) || buttonText.getText().toLowerCase(Locale.ROOT).contains(word); diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorDraggableList.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorDraggableList.java index 73a021541..c249feb31 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorDraggableList.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorDraggableList.java @@ -22,6 +22,7 @@ import io.github.notenoughupdates.moulconfig.GuiTextures; import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.common.MoulConfigPair; import io.github.notenoughupdates.moulconfig.common.text.StructuredText; import io.github.notenoughupdates.moulconfig.gui.GuiComponent; import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; @@ -30,7 +31,6 @@ import io.github.notenoughupdates.moulconfig.internal.*; import io.github.notenoughupdates.moulconfig.observer.GetSetter; import io.github.notenoughupdates.moulconfig.processor.ProcessedOption; -import kotlin.Pair; import lombok.var; import org.jetbrains.annotations.NotNull; @@ -46,7 +46,7 @@ public class GuiOptionEditorDraggableList extends ComponentEditor { private int dragStartIndex = -1; private LerpingInteger2 trashAnimation = new LerpingInteger2(255, 3, 2); - private Pair lastListRenderPos = new Pair<>(0, 0); + private MoulConfigPair lastListRenderPos = new MoulConfigPair<>(0, 0); private Enum[] enumConstants; private String exampleTextConcat; @@ -198,7 +198,7 @@ public boolean mouseEvent(@NotNull MouseEvent mouseEvent, @NotNull GuiImmediateC @Override public void render(@NotNull GuiImmediateContext context) { - lastListRenderPos = new Pair<>(context.getRenderOffsetX(), context.getRenderOffsetY()); + lastListRenderPos = new MoulConfigPair<>(context.getRenderOffsetX(), context.getRenderOffsetY()); var renderContext = context.getRenderContext(); var width = context.getWidth(); var fr = IMinecraft.INSTANCE.getDefaultFontRenderer(); diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorSlider.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorSlider.java index ea873bbe7..7376f4f86 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorSlider.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorSlider.java @@ -13,7 +13,7 @@ public GuiOptionEditorSlider(ProcessedOption option, float minValue, float maxVa super(option); if (minStep < 0) minStep = 0.01f; - component = wrapComponent(new SliderWithTextComponent((GetSetter) option.intoProperty(), minValue, maxValue, minStep, 55)); + component = wrapComponent(new SliderWithTextComponent((GetSetter) option.intoProperty(), minValue, maxValue, minStep, 55)); } public float getFloatValue() { diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.java new file mode 100644 index 000000000..108b368b1 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.java @@ -0,0 +1,52 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import java.lang.reflect.Field; +import java.util.Objects; + +public final class BoundField { + private final Field field; + private final Object boundTo; + + public BoundField(Field field, Object boundTo) { + this.field = field; + this.boundTo = boundTo; + } + + public Field getField() { + return field; + } + + public Object getBoundTo() { + return boundTo; + } + + public Field component1() { + return field; + } + + public Object component2() { + return boundTo; + } + + public BoundField copy(Field field, Object boundTo) { + return new BoundField(field, boundTo); + } + + @Override + public String toString() { + return field + " bound to " + boundTo; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BoundField)) return false; + BoundField that = (BoundField) o; + return Objects.equals(field, that.field) && Objects.equals(boundTo, that.boundTo); + } + + @Override + public int hashCode() { + return Objects.hash(field, boundTo); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.kt deleted file mode 100644 index 0272b7b2d..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/BoundField.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import java.lang.reflect.Field - -data class BoundField( - val field: Field, - val boundTo: Any -) { - override fun toString(): String { - return "$field bound to $boundTo" - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.java new file mode 100644 index 000000000..c5bffafc6 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.java @@ -0,0 +1,54 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.RenderContext; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; + +public final class DrawContextExt { + public static final DrawContextExt INSTANCE = new DrawContextExt(); + + private DrawContextExt() { + } + + public static void drawStringCenteredScalingDownWithMaxWidth( + RenderContext context, + StructuredText text, + int centerX, + int centerY, + int maxWidth, + int color + ) { + drawStringCenteredScalingDownWithMaxWidth(context, text, centerX, centerY, maxWidth, color, false, context.getMinecraft().getDefaultFontRenderer()); + } + + public static void drawStringCenteredScalingDownWithMaxWidth( + RenderContext context, + StructuredText text, + int centerX, + int centerY, + int maxWidth, + int color, + boolean shadow + ) { + drawStringCenteredScalingDownWithMaxWidth(context, text, centerX, centerY, maxWidth, color, shadow, context.getMinecraft().getDefaultFontRenderer()); + } + + public static void drawStringCenteredScalingDownWithMaxWidth( + RenderContext context, + StructuredText text, + int centerX, + int centerY, + int maxWidth, + int color, + boolean shadow, + IFontRenderer fr + ) { + context.pushMatrix(); + int width = fr.getStringWidth(text); + float factor = Math.min(maxWidth / (float) width, 1F); + context.translate((float) centerX, (float) centerY); + context.scale(factor, factor); + context.drawString(fr, text, -width / 2, -fr.getHeight() / 2, color, shadow); + context.popMatrix(); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.kt deleted file mode 100644 index 60cd7731b..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/DrawContextExt.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.common.IFontRenderer -import io.github.notenoughupdates.moulconfig.common.RenderContext -import io.github.notenoughupdates.moulconfig.common.text.StructuredText - -object DrawContextExt { - @JvmOverloads - @JvmStatic - fun RenderContext.drawStringCenteredScalingDownWithMaxWidth( - text: StructuredText, - centerX: Int, - centerY: Int, - maxWidth: Int, - color: Int, - shadow: Boolean = false, - fr: IFontRenderer = minecraft.defaultFontRenderer, - ) { - pushMatrix() - val width = fr.getStringWidth(text) - val factor = (maxWidth / width.toFloat()).coerceAtMost(1f) - translate(centerX.toFloat(), centerY.toFloat()) - scale(factor, factor) - drawString(fr, text, -width / 2, -fr.height / 2, color, shadow) - popMatrix() - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.java new file mode 100644 index 000000000..4677c2a30 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.java @@ -0,0 +1,9 @@ +package io.github.notenoughupdates.moulconfig.internal; + +public interface MCLogger { + void warn(String text); + + void info(String text); + + void error(String text, Throwable throwable); +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.kt deleted file mode 100644 index 40bbff3be..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/MCLogger.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -interface MCLogger { - fun warn(text: String) - fun info(text: String) - fun error(text: String, throwable: Throwable) -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.java new file mode 100644 index 000000000..01cc8c82a --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.java @@ -0,0 +1,17 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.RenderContext; +import juuxel.libninepatch.ContextualTextureRenderer; + +public final class NinePatchRenderer implements ContextualTextureRenderer { + public static final NinePatchRenderer INSTANCE = new NinePatchRenderer(); + + private NinePatchRenderer() { + } + + @Override + public void draw(MyResourceLocation texture, RenderContext context, int x, int y, int width, int height, float u1, float v1, float u2, float v2) { + context.drawComplexTexture(texture, (float) x, (float) y, (float) width, (float) height, draw -> draw.uv(u1, v1, u2, v2)); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.kt deleted file mode 100644 index 968a78a3c..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/NinePatchRenderer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.common.MyResourceLocation -import io.github.notenoughupdates.moulconfig.common.RenderContext -import juuxel.libninepatch.ContextualTextureRenderer - -object NinePatchRenderer : ContextualTextureRenderer { - override fun draw( - texture: MyResourceLocation, - context: RenderContext, - x: Int, - y: Int, - width: Int, - height: Int, - u1: Float, - v1: Float, - u2: Float, - v2: Float - ) { - context.drawComplexTexture( - texture, - x.toFloat(), - y.toFloat(), - width.toFloat(), - height.toFloat(), - ) { - it.uv(u1, v1, u2, v2) - } - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/StackUtil.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/StackUtil.java index 0067cf95a..35d765663 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/StackUtil.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/StackUtil.java @@ -58,11 +58,15 @@ public static Predicate defaultSkips() { it.getClassName().equals(StackUtil.class.getName()) || it.getClassName().equals(Warnings.class.getName()) || it.getClassName().startsWith("java.") - || it.getClassName().startsWith("kotlin."); + || it.getClassName().startsWith(kotlinPackage()); } public static final String MOULCONFIG_BASE_PACKAGE = GuiTextures.class.getPackage().getName(); + private static String kotlinPackage() { + return new String(new char[]{'k', 'o', 't', 'l', 'i', 'n', '.'}); + } + public static StackUtil getWalker() { return new StackUtil(new Exception().getStackTrace()).skip(1); } diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/Warnings.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/Warnings.java index 271586ecc..21e15b968 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/Warnings.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/internal/Warnings.java @@ -59,7 +59,7 @@ private static void warn0(@NotNull String warningText, int depth) { for (StackTraceElement stackTraceElement : stackTrace) { if (i++ < depth || stackTraceElement.getClassName().startsWith("java.") - || stackTraceElement.getClassName().startsWith("kotlin.") + || stackTraceElement.getClassName().startsWith(kotlinPackage()) || (stackTraceElement.getClassName().startsWith(basePackage) && !stackTraceElement.getClassName().startsWith(testPackage))) continue; @@ -73,6 +73,10 @@ public static void warn(@NotNull String warningText, int depth) { if (shouldWarn) warn0(warningText, depth); } + private static String kotlinPackage() { + return new String(new char[]{'k', 'o', 't', 'l', 'i', 'n', '.'}); + } + public static void warn(@NotNull String warningText) { warn(warningText, 4); } diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.java new file mode 100644 index 000000000..ab261421c --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.java @@ -0,0 +1,9 @@ +package io.github.notenoughupdates.moulconfig.managed; + +public interface DataMapper { + String serialize(T value); + + T createDefault(); + + T deserialize(String string); +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.kt deleted file mode 100644 index f482ceea6..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/DataMapper.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -interface DataMapper { - fun serialize(value: T): String - fun createDefault(): T - fun deserialize(string: String): T -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.java new file mode 100644 index 000000000..0161b59a5 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.java @@ -0,0 +1,69 @@ +package io.github.notenoughupdates.moulconfig.managed; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.github.notenoughupdates.moulconfig.ChromaColour; +import io.github.notenoughupdates.moulconfig.LegacyStringChromaColourTypeAdapter; +import io.github.notenoughupdates.moulconfig.observer.PropertyTypeAdapterFactory; + +public class GsonMapper implements DataMapper { + private final Class clazz; + private final GsonBuilder gsonBuilder = new GsonBuilder() + .registerTypeAdapterFactory(new PropertyTypeAdapterFactory()) + .registerTypeAdapter(ChromaColour.class, new LegacyStringChromaColourTypeAdapter(true)); + private boolean doNotRequireExposed; + private Gson gson; + + public GsonMapper(Class clazz) { + this.clazz = clazz; + } + + public Class getClazz() { + return clazz; + } + + public GsonBuilder getGsonBuilder() { + return gsonBuilder; + } + + public boolean getDoNotRequireExposed() { + return doNotRequireExposed; + } + + public boolean isDoNotRequireExposed() { + return doNotRequireExposed; + } + + public void setDoNotRequireExposed(boolean doNotRequireExposed) { + this.doNotRequireExposed = doNotRequireExposed; + } + + private Gson getGson() { + if (gson == null) { + if (!doNotRequireExposed) { + gsonBuilder.excludeFieldsWithoutExposeAnnotation(); + } + gson = gsonBuilder.create(); + } + return gson; + } + + @Override + public String serialize(T value) { + return getGson().toJson(value); + } + + @Override + public T createDefault() { + try { + return clazz.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public T deserialize(String string) { + return getGson().fromJson(string, clazz); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.kt deleted file mode 100644 index 0c931d5f4..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/GsonMapper.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -import com.google.gson.GsonBuilder -import io.github.notenoughupdates.moulconfig.ChromaColour -import io.github.notenoughupdates.moulconfig.LegacyStringChromaColourTypeAdapter -import io.github.notenoughupdates.moulconfig.observer.PropertyTypeAdapterFactory - -class GsonMapper(val clazz: Class) : DataMapper { - val gsonBuilder = GsonBuilder() - .registerTypeAdapterFactory(PropertyTypeAdapterFactory()) - .registerTypeAdapter(ChromaColour::class.java, LegacyStringChromaColourTypeAdapter(true)) - var doNotRequireExposed = false - - private val gson by lazy { - if (!doNotRequireExposed) { - gsonBuilder.excludeFieldsWithoutExposeAnnotation() - } - gsonBuilder.create() - } - override fun serialize(value: T): String { - return gson.toJson(value) - } - - override fun createDefault(): T { - return clazz.newInstance() - } - - override fun deserialize(string: String): T { - return gson.fromJson(string, clazz) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.java new file mode 100644 index 000000000..f770227da --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.java @@ -0,0 +1,97 @@ +package io.github.notenoughupdates.moulconfig.managed; + +import io.github.notenoughupdates.moulconfig.Config; +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor; +import io.github.notenoughupdates.moulconfig.gui.MoulConfigEditor; +import io.github.notenoughupdates.moulconfig.processor.BuiltinMoulConfigGuis; +import io.github.notenoughupdates.moulconfig.processor.ConfigProcessorDriver; +import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor; +import io.github.notenoughupdates.moulconfig.processor.ProcessedOption; + +import java.io.File; +import java.lang.annotation.Annotation; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +public class ManagedConfig extends ManagedDataFile { + private final ManagedConfigBuilder builder; + private MoulConfigProcessor processor; + + @SuppressWarnings("unchecked") + public ManagedConfig(ManagedConfigBuilder builder) { + super(prepare(builder)); + this.builder = builder; + } + + private static ManagedConfigBuilder prepare(ManagedConfigBuilder builder) { + builder.setAfterLoad(((Consumer>) file -> ((ManagedConfig) file).rebuildConfigProcessor(builder)).andThen(builder.getAfterLoad())); + return builder; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static ManagedConfig create(File file, Class clazz) { + ManagedConfigBuilder builder = new ManagedConfigBuilder(file, clazz); + return new ManagedConfig(builder); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static ManagedConfig create(File file, Class clazz, Consumer consumer) { + ManagedConfigBuilder builder = new ManagedConfigBuilder(file, clazz); + consumer.accept(builder); + return new ManagedConfig(builder); + } + + public MoulConfigProcessor getProcessor() { + return processor; + } + + @Override + public void injectIntoInstance() { + if (getInstance().saveRunnables != null) { + getInstance().saveRunnables.add(this::saveToFile); + } + } + + public void rebuildConfigProcessor() { + rebuildConfigProcessor(builder); + } + + private void rebuildConfigProcessor(ManagedConfigBuilder builder) { + processor = buildProcessor(builder); + } + + /** + * Helper function to introduce the {@code A} type parameter so that two objects can be cast to be that same {@code A} variable. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private static void cast( + MoulConfigProcessor processor, + Class annotation, + BiFunction method + ) { + processor.registerConfigEditor((Class) annotation, (BiFunction) method); + } + + private MoulConfigProcessor buildProcessor(ManagedConfigBuilder builder) { + MoulConfigProcessor processor = new MoulConfigProcessor<>(getInstance()); + if (builder.getUseDefaultProcessors()) { + BuiltinMoulConfigGuis.addProcessors(processor); + } + for (ManagedConfigBuilder.CustomProcessor customProcessor : builder.getCustomProcessors()) { + cast(processor, customProcessor.annotation, customProcessor.method); + } + ConfigProcessorDriver driver = new ConfigProcessorDriver(processor); + driver.checkExpose = builder.getCheckExpose(); + driver.processConfig(getInstance()); + return processor; + } + + public MoulConfigEditor getEditor() { + return new MoulConfigEditor<>(processor); + } + + public void openConfigGui() { + IMinecraft.INSTANCE.openWrappedScreen(getEditor()); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.kt deleted file mode 100644 index 7e95cb98f..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfig.kt +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -import io.github.notenoughupdates.moulconfig.Config -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor -import io.github.notenoughupdates.moulconfig.gui.MoulConfigEditor -import io.github.notenoughupdates.moulconfig.processor.BuiltinMoulConfigGuis -import io.github.notenoughupdates.moulconfig.processor.ConfigProcessorDriver -import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor -import io.github.notenoughupdates.moulconfig.processor.ProcessedOption -import java.io.File -import java.util.function.BiFunction -import java.util.function.Consumer - -class ManagedConfig(private val builder: ManagedConfigBuilder) : - ManagedDataFile(builder.apply { - afterLoad = Consumer> { (it as ManagedConfig).rebuildConfigProcessor(builder) } - .andThen(afterLoad) - }) { - - companion object { - @JvmStatic - @JvmOverloads - fun create( - file: File, - clazz: Class, - consumer: (ManagedConfigBuilder.() -> Unit) = {} - ): ManagedConfig { - return ManagedConfig(ManagedConfigBuilder(file, clazz).apply(consumer)) - } - } - - lateinit var processor: MoulConfigProcessor - private set - - override fun injectIntoInstance() { - instance.saveRunnables?.add(this::saveToFile) - } - - fun rebuildConfigProcessor() { - rebuildConfigProcessor(builder) - } - - private fun rebuildConfigProcessor(builder: ManagedConfigBuilder) { - processor = buildProcessor(builder) - } - - /** - * Helper function to introduce the A type parameter so that two objects can be cast to be that same A variable. - */ - @Suppress("NOTHING_TO_INLINE") - private inline fun cast( - processor: MoulConfigProcessor, - annotation: Class, - method: Any - ) { - @Suppress("UNCHECKED_CAST") - processor.registerConfigEditor( - annotation, - method as BiFunction - ) - } - - private fun buildProcessor(builder: ManagedConfigBuilder): MoulConfigProcessor { - val processor = - MoulConfigProcessor(this.instance) - if (builder.useDefaultProcessors) { - BuiltinMoulConfigGuis.addProcessors( - processor - ) - } - builder.customProcessors.forEach { (annotation, method) -> - cast(processor, annotation, method) - } - val driver = ConfigProcessorDriver(processor) - driver.checkExpose = builder.checkExpose - driver.processConfig(this.instance) - return processor - } - - fun getEditor(): MoulConfigEditor { - return MoulConfigEditor(processor) - } - - fun openConfigGui() { - IMinecraft.INSTANCE.openWrappedScreen(getEditor()) - } -} - diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.java new file mode 100644 index 000000000..b182b1976 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.java @@ -0,0 +1,53 @@ +package io.github.notenoughupdates.moulconfig.managed; + +import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor; +import io.github.notenoughupdates.moulconfig.processor.ProcessedOption; + +import java.io.File; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +public class ManagedConfigBuilder extends ManagedDataFileBuilder { + static final class CustomProcessor { + final Class annotation; + final BiFunction method; + + CustomProcessor(Class annotation, BiFunction method) { + this.annotation = annotation; + this.method = method; + } + } + + private boolean useDefaultProcessors = true; + private boolean checkExpose = true; + private final List customProcessors = new ArrayList<>(); + + public ManagedConfigBuilder(File file, Class clazz) { + super(file, clazz); + } + + public boolean getUseDefaultProcessors() { return useDefaultProcessors; } + public boolean isUseDefaultProcessors() { return useDefaultProcessors; } + public void setUseDefaultProcessors(boolean useDefaultProcessors) { this.useDefaultProcessors = useDefaultProcessors; } + public boolean getCheckExpose() { return checkExpose; } + public boolean isCheckExpose() { return checkExpose; } + public void setCheckExpose(boolean checkExpose) { this.checkExpose = checkExpose; } + List getCustomProcessors() { return customProcessors; } + + public void clearCustomProcessors() { + customProcessors.clear(); + } + + @SuppressWarnings("unchecked") + public void customProcessor( + Class annotation, + BiFunction editorGenerator + ) { + customProcessors.add(new CustomProcessor( + annotation, + (BiFunction) editorGenerator + )); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.kt deleted file mode 100644 index 84eab3c2e..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedConfigBuilder.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -import io.github.notenoughupdates.moulconfig.gui.GuiOptionEditor -import io.github.notenoughupdates.moulconfig.processor.ProcessedOption -import java.io.File -import java.util.function.BiFunction - -class ManagedConfigBuilder(file: File, clazz: Class) : ManagedDataFileBuilder(file, clazz) { - var useDefaultProcessors = true - var checkExpose = true - internal val customProcessors = - mutableListOf, BiFunction>>() - - fun clearCustomProcessors() { - customProcessors.clear() - } - - inline fun customProcessor(noinline editorGenerator: (ProcessedOption, A) -> GuiOptionEditor) { - customProcessor(A::class.java, editorGenerator) - } - - fun customProcessor( - annotation: Class, - editorGenerator: BiFunction - ) { - @Suppress("UNCHECKED_CAST") - customProcessors.add( - Pair( - annotation, - editorGenerator as BiFunction - ) - ) - } - -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.java new file mode 100644 index 000000000..f3076cb45 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.java @@ -0,0 +1,115 @@ +package io.github.notenoughupdates.moulconfig.managed; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ManagedDataFile { + private final File file; + private final DataMapper mapper; + private final BiConsumer, Exception> loadFailed; + private final BiConsumer, Exception> saveFailed; + private final Consumer> beforeLoad; + private final Consumer> afterLoad; + private final Consumer> beforeSave; + private final Consumer> afterSave; + private T instance; + + ManagedDataFile( + File file, + DataMapper mapper, + BiConsumer, Exception> loadFailed, + BiConsumer, Exception> saveFailed, + Consumer> beforeLoad, + Consumer> afterLoad, + Consumer> beforeSave, + Consumer> afterSave + ) { + this.file = file; + this.mapper = mapper; + this.loadFailed = loadFailed; + this.saveFailed = saveFailed; + this.beforeLoad = beforeLoad; + this.afterLoad = afterLoad; + this.beforeSave = beforeSave; + this.afterSave = afterSave; + this.instance = mapper.createDefault(); + File parent = file.getAbsoluteFile().getParentFile(); + if (parent != null) { + parent.mkdirs(); + } + reloadFromFile(); + } + + public ManagedDataFile(ManagedDataFileBuilder builder) { + this( + builder.getFile(), + builder.getMapper(), + builder.getLoadFailed(), + builder.getSaveFailed(), + builder.getBeforeLoad(), + builder.getAfterLoad(), + builder.getBeforeSave(), + builder.getAfterSave() + ); + } + + public File getFile() { return file; } + public DataMapper getMapper() { return mapper; } + public T getInstance() { return instance; } + public void setInstance(T instance) { this.instance = instance; } + + public void reloadFromFile() { + beforeLoad.accept(this); + try { + if (file.exists()) { + instance = mapper.deserialize(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); + } else { + instance = mapper.createDefault(); + } + } catch (Exception ex) { + loadFailed.accept(this, ex); + } + injectIntoInstance(); + afterLoad.accept(this); + } + + public void injectIntoInstance() { + } + + private File createUniqueExtraFile(String identifier) { + return createUniqueExtraFile(identifier, file.getParentFile()); + } + + private File createUniqueExtraFile(String identifier, File directory) { + int jvmHash = ManagementFactory.getRuntimeMXBean().getName().hashCode(); + String timestamp = Long.toString(System.currentTimeMillis()); + if (directory != null) { + directory.mkdirs(); + } + String fileName = file.getName(); + int dot = fileName.lastIndexOf('.'); + String nameWithoutExtension = dot >= 0 ? fileName.substring(0, dot) : fileName; + String extension = dot >= 0 ? fileName.substring(dot + 1) : ""; + return new File(directory, nameWithoutExtension + "-" + jvmHash + "-" + timestamp + "-" + identifier + "." + extension); + } + + public void saveToFile() { + beforeSave.accept(this); + String toSave = mapper.serialize(instance); + File temporarySaveFile = createUniqueExtraFile("save"); + try { + Files.write(temporarySaveFile.toPath(), toSave.getBytes(StandardCharsets.UTF_8)); + mapper.deserialize(new String(Files.readAllBytes(temporarySaveFile.toPath()), StandardCharsets.UTF_8)); + Files.move(temporarySaveFile.toPath(), file.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } catch (Exception ex) { + saveFailed.accept(this, ex); + } + afterSave.accept(this); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.kt deleted file mode 100644 index 0438cf178..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFile.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -import java.io.File -import java.lang.management.ManagementFactory -import java.nio.file.Files -import java.nio.file.StandardCopyOption -import java.util.function.BiConsumer -import java.util.function.Consumer - -open class ManagedDataFile internal constructor( - val file: File, - val mapper: DataMapper, - private val loadFailed: BiConsumer, Exception>, - private val saveFailed: BiConsumer, Exception>, - private val beforeLoad: Consumer>, - private val afterLoad: Consumer>, - private val beforeSave: Consumer>, - private val afterSave: Consumer>, -) { - constructor(builder: ManagedDataFileBuilder) : this( - builder.file, - builder.mapper, - builder.loadFailed, - builder.saveFailed, - builder.beforeLoad, - builder.afterLoad, - builder.beforeSave, - builder.afterSave - ) - - companion object { - @JvmStatic - @JvmOverloads - fun create( - file: File, - clazz: Class, - consumer: (ManagedDataFileBuilder.() -> Unit) = {} - ): ManagedDataFile { - return ManagedDataFile(ManagedDataFileBuilder(file, clazz).apply(consumer)) - } - } - - var instance: T = mapper.createDefault() - - init { - file.absoluteFile.parentFile.mkdirs() - reloadFromFile() - } - - fun reloadFromFile() { - beforeLoad.accept(this) - try { - if (file.exists()) { - instance = mapper.deserialize(file.readText()) - } else { - instance = mapper.createDefault() - } - } catch (ex: Exception) { - loadFailed.accept(this, ex) - } - injectIntoInstance() - afterLoad.accept(this) - } - - open fun injectIntoInstance() {} - - private fun createUniqueExtraFile(identifier: String, directory: File = file.parentFile): File { - val jvmHash = ManagementFactory.getRuntimeMXBean().name.hashCode() - val timestamp = System.currentTimeMillis().toString() - directory.mkdirs() - return directory.resolve("${file.nameWithoutExtension}-${jvmHash}-${timestamp}-$identifier.${file.extension}") - } - - fun saveToFile() { - beforeSave.accept(this) - val toSave = mapper.serialize(instance) - val temporarySaveFile = createUniqueExtraFile("save") - try { - temporarySaveFile.writeText(toSave) - mapper.deserialize(temporarySaveFile.readText()) - Files.move( - temporarySaveFile.toPath(), - file.toPath(), - StandardCopyOption.ATOMIC_MOVE, - StandardCopyOption.REPLACE_EXISTING - ) - } catch (ex: Exception) { - saveFailed.accept(this, ex) - } - afterSave.accept(this) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.java new file mode 100644 index 000000000..c1805814e --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.java @@ -0,0 +1,60 @@ +package io.github.notenoughupdates.moulconfig.managed; + +import java.io.File; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ManagedDataFileBuilder { + private File file; + private final Class clazz; + private DataMapper mapper; + private BiConsumer, Exception> loadFailed = (file, ex) -> {}; + private BiConsumer, Exception> saveFailed = (file, ex) -> {}; + private Consumer> beforeLoad = file -> {}; + private Consumer> afterLoad = file -> {}; + private Consumer> beforeSave = file -> {}; + private Consumer> afterSave = file -> {}; + + public ManagedDataFileBuilder(File file, Class clazz) { + this.file = file; + this.clazz = clazz; + this.mapper = new GsonMapper<>(clazz); + } + + public void throwOnFailure() { + loadFailed = (file, ex) -> { + throw new RuntimeException(ex); + }; + saveFailed = (file, ex) -> { + throw new RuntimeException(ex); + }; + } + + public void jsonMapper() { + jsonMapper(mapper -> {}); + } + + public void jsonMapper(Consumer> function) { + GsonMapper gsonMapper = new GsonMapper<>(clazz); + function.accept(gsonMapper); + mapper = gsonMapper; + } + + public File getFile() { return file; } + public void setFile(File file) { this.file = file; } + public Class getClazz() { return clazz; } + public DataMapper getMapper() { return mapper; } + public void setMapper(DataMapper mapper) { this.mapper = mapper; } + public BiConsumer, Exception> getLoadFailed() { return loadFailed; } + public void setLoadFailed(BiConsumer, Exception> loadFailed) { this.loadFailed = loadFailed; } + public BiConsumer, Exception> getSaveFailed() { return saveFailed; } + public void setSaveFailed(BiConsumer, Exception> saveFailed) { this.saveFailed = saveFailed; } + public Consumer> getBeforeLoad() { return beforeLoad; } + public void setBeforeLoad(Consumer> beforeLoad) { this.beforeLoad = beforeLoad; } + public Consumer> getAfterLoad() { return afterLoad; } + public void setAfterLoad(Consumer> afterLoad) { this.afterLoad = afterLoad; } + public Consumer> getBeforeSave() { return beforeSave; } + public void setBeforeSave(Consumer> beforeSave) { this.beforeSave = beforeSave; } + public Consumer> getAfterSave() { return afterSave; } + public void setAfterSave(Consumer> afterSave) { this.afterSave = afterSave; } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.kt deleted file mode 100644 index 6d3754678..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/managed/ManagedDataFileBuilder.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.managed - -import java.io.File -import java.util.function.BiConsumer -import java.util.function.Consumer - -open class ManagedDataFileBuilder( - var file: File, - val clazz: Class -) { - - fun throwOnFailure() { - loadFailed = BiConsumer { _, ex -> throw ex } - saveFailed = BiConsumer { _, ex -> throw ex } - } - - var mapper: DataMapper = GsonMapper(clazz) - - @JvmOverloads - fun jsonMapper(function: GsonMapper.() -> Unit = {}) { - mapper = GsonMapper(clazz).also(function) - } - - open var loadFailed: BiConsumer, Exception> = BiConsumer { _, _ -> } - open var saveFailed: BiConsumer, Exception> = BiConsumer { _, _ -> } - open var beforeLoad: Consumer> = Consumer {} - open var afterLoad: Consumer> = Consumer {} - open var beforeSave: Consumer> = Consumer {} - open var afterSave: Consumer> = Consumer {} -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImpl.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImpl.java index 82c3a08c3..af68b09c5 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImpl.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImpl.java @@ -122,12 +122,7 @@ public Type getType() { @Override public boolean set(Object value) { try { - Object toSet; - if (getType() == int.class && value instanceof Number) { - toSet = ((Number) value).intValue(); - } else { - toSet = value; - } + Object toSet = coerceValue(value); if (isProperty) { ((Property) field.get(container)).set(toSet); } else { @@ -140,6 +135,31 @@ public boolean set(Object value) { } } + private Object coerceValue(Object value) { + Type type = getType(); + if (!(value instanceof Number) || !(type instanceof Class)) { + return value; + } + + Number number = (Number) value; + Class targetType = (Class) type; + if (targetType == int.class || targetType == Integer.class) { + return number.intValue(); + } else if (targetType == float.class || targetType == Float.class) { + return number.floatValue(); + } else if (targetType == double.class || targetType == Double.class) { + return number.doubleValue(); + } else if (targetType == long.class || targetType == Long.class) { + return number.longValue(); + } else if (targetType == short.class || targetType == Short.class) { + return number.shortValue(); + } else if (targetType == byte.class || targetType == Byte.class) { + return number.byteValue(); + } else { + return value; + } + } + @Override public void explicitNotifyChange() { if (isProperty) { diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.java new file mode 100644 index 000000000..60cc3bf32 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.java @@ -0,0 +1,8 @@ +package io.github.notenoughupdates.moulconfig.xml; + +public enum ChildCount { + NONE, + ONE, + ANY, + TWO +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.kt deleted file mode 100644 index ce5017e87..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/ChildCount.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml - -enum class ChildCount { - NONE, - ONE, - ANY, - TWO, - - ; -} \ No newline at end of file diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.java new file mode 100644 index 000000000..5be908076 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.java @@ -0,0 +1,118 @@ +package io.github.notenoughupdates.moulconfig.xml; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.internal.CollectionUtils; +import io.github.notenoughupdates.moulconfig.internal.Warnings; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import org.w3c.dom.Element; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class XMLContext { + private final XMLUniverse universe; + private final T boundObject; + + public XMLContext(XMLUniverse universe, T boundObject) { + this.universe = universe; + this.boundObject = boundObject; + } + + public XMLUniverse getUniverse() { + return universe; + } + + public T getBoundObject() { + return boundObject; + } + + public GuiComponent getChildFragment(Element element) { + return CollectionUtils.getSingleOrThrow(getChildFragments(element, this)); + } + + public GuiComponent getChildFragment(Element element, Object rebind) { + return CollectionUtils.getSingleOrThrow(getChildFragments(element, new XMLContext<>(universe, rebind))); + } + + public List getChildFragments(Element element) { + return getChildFragments(element, this); + } + + public List getChildFragments(Element element, Object rebind) { + return getChildFragments(element, new XMLContext<>(universe, rebind)); + } + + public List getChildFragments(Element element, XMLContext context) { + org.w3c.dom.NodeList childNodes = element.getChildNodes(); + List list = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + org.w3c.dom.Node item = childNodes.item(i); + if (item instanceof Element) { + list.add(universe.load(context, (Element) item)); + } + } + return list; + } + + public E getPropertyFromAttribute(Element element, QName name, Class type, E def) { + GetSetter prop = getPropertyFromAttribute(element, name, type); + return prop == null ? def : prop.get(); + } + + private String getRawXMLValue(Element element, QName name) { + if (!XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI())) { + Warnings.warn("Attributes should not have a namespace attached to them. This namespace will be ignored"); + } + String attributeValue = element.getAttribute(name.getLocalPart()); + return attributeValue.isEmpty() ? null : attributeValue; + } + + public GetSetter getPropertyFromAttribute(Element element, QName name, Class type) { + String attributeValue = getRawXMLValue(element, name); + if (attributeValue == null) return null; + if (attributeValue.startsWith("@")) { + return getBoundProperty(attributeValue.substring(1), type); + } + E e = universe.mapXMLObject(attributeValue, type); + return new GetSetter() { + @Override + public E get() { + return e; + } + + @Override + public void set(E newValue) { + throw new UnsupportedOperationException(); + } + }; + } + + public Consumer getMethodFromAttribute(Element element, QName name, Class type) { + String attribute = getRawXMLValue(element, name); + if (attribute == null) return value -> {}; + if (!attribute.startsWith("@")) throw new RuntimeException("Object bound method without @ prefix " + attribute + " at " + name); + return getBoundMethod(attribute.substring(1), type); + } + + public Runnable getMethodFromAttribute(Element element, QName name) { + String attribute = getRawXMLValue(element, name); + if (attribute == null) return () -> {}; + if (!attribute.startsWith("@")) throw new RuntimeException("Object bound method without @ prefix " + attribute + " at " + name); + return getBoundMethod(attribute.substring(1)); + } + + public Consumer getBoundMethod(String name, Class argument) { + return universe.getPropertyFinder(boundObject.getClass()).getBoundFunction(name, boundObject, argument); + } + + public Runnable getBoundMethod(String name) { + return universe.getPropertyFinder(boundObject.getClass()).getBoundFunction(name, boundObject); + } + + public GetSetter getBoundProperty(String name, Class type) { + return universe.getPropertyFinder(boundObject.getClass()).getBoundProperty(name, type, boundObject); + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.kt deleted file mode 100644 index 2670b1200..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLContext.kt +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.internal.CollectionUtils -import io.github.notenoughupdates.moulconfig.internal.Warnings -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import lombok.Getter -import lombok.RequiredArgsConstructor -import org.w3c.dom.Element -import java.util.function.Consumer -import javax.xml.XMLConstants -import javax.xml.namespace.QName - -@RequiredArgsConstructor -@Getter -class XMLContext( - val universe: XMLUniverse, - val boundObject: T, -) { - fun getChildFragment(element: Element): GuiComponent { - return CollectionUtils.getSingleOrThrow( - getChildFragments(element, this) - ) - } - - fun getChildFragment( - element: Element, - rebind: Any - ): GuiComponent { - return CollectionUtils.getSingleOrThrow( - getChildFragments(element, XMLContext(universe, rebind)) - ) - } - - fun getChildFragments(element: Element): List { - return getChildFragments(element, this) - } - - fun getChildFragments( - element: Element, - rebind: Any - ): List { - return getChildFragments(element, XMLContext(universe, rebind)) - } - - fun getChildFragments( - element: Element, - context: XMLContext<*> - ): List { - val childNodes = element.childNodes - val list: MutableList = ArrayList() - for (i in 0 until childNodes.length) { - val item = childNodes.item(i) - if (item is Element) { - val loadedFragment = universe.load(context, item) - list.add(loadedFragment) - } - } - return list - } - - fun getPropertyFromAttribute(element: Element, name: QName, type: Class, def: E): E { - val prop = getPropertyFromAttribute(element, name, type) ?: return def - return prop.get() - } - - private fun getRawXMLValue(element: Element, name: QName): String? { - if (name.namespaceURI != XMLConstants.NULL_NS_URI) { - Warnings.warn("Attributes should not have a namespace attached to them. This namespace will be ignored") - } - val attributeValue = element.getAttribute(name.localPart) - return if (attributeValue.isEmpty()) null else attributeValue - } - - fun getPropertyFromAttribute( - element: Element, - name: QName, - type: Class - ): GetSetter? { - val attributeValue = getRawXMLValue(element, name) ?: return null - if (attributeValue.startsWith("@")) { - return getBoundProperty(attributeValue.substring(1), type) - } - val e = universe.mapXMLObject(attributeValue, type) - return object : GetSetter { - override fun get(): E { - return e - } - - override fun set(newValue: E) { - throw UnsupportedOperationException() - } - } - } - - fun getMethodFromAttribute(element: Element, name: QName, type: Class): Consumer { - val attribute = getRawXMLValue(element, name) ?: return Consumer { } - if (!attribute.startsWith("@")) throw RuntimeException("Object bound method without @ prefix $attribute at $name") - return getBoundMethod(attribute.substring(1), type) - } - - fun getMethodFromAttribute(element: Element, name: QName): Runnable { - val attribute = getRawXMLValue(element, name) ?: return Runnable {} - if (!attribute.startsWith("@")) throw RuntimeException("Object bound method without @ prefix $attribute at $name") - return getBoundMethod(attribute.substring(1)) - } - - fun getBoundMethod(name: String, argument: Class): Consumer { - return universe.getPropertyFinder(boundObject.javaClass).getBoundFunction(name, boundObject, argument) - } - - fun getBoundMethod(name: String): Runnable { - return universe.getPropertyFinder(boundObject.javaClass).getBoundFunction(name, boundObject) - } - - fun getBoundProperty( - name: String, - type: Class - ): GetSetter { - val propertyFinder = universe.getPropertyFinder(boundObject.javaClass) - return propertyFinder.getBoundProperty(name, type, boundObject) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLUniverse.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLUniverse.java index 9c7397e7b..5e55f7e58 100644 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLUniverse.java +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XMLUniverse.java @@ -82,7 +82,7 @@ public static XMLUniverse getDefaultUniverse() { xmlUniverse.registerTypeMorphism(new UnboxPrimitives()); IMinecraft.getInstance().registerPlatformTypeMorphisms(xmlUniverse); xmlUniverse.registerMapper(List.class, str -> Arrays.asList(str.split(";"))); - xmlUniverse.registerMapper(MyResourceLocation.class, MyResourceLocation.Companion::parse); + xmlUniverse.registerMapper(MyResourceLocation.class, MyResourceLocation::parse); xmlUniverse.registerMapper(PanelComponent.BackgroundRenderer.class, PanelComponent.DefaultBackgroundRenderer::valueOf); xmlUniverse.registerMapper(HorizontalAlign.class, HorizontalAlign::valueOf); xmlUniverse.registerMapper(VerticalAlign.class, VerticalAlign::valueOf); diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.java new file mode 100644 index 000000000..f8786187c --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.java @@ -0,0 +1,144 @@ +package io.github.notenoughupdates.moulconfig.xml; + +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; + +public class XSDGenerator { + private final XMLUniverse universe; + private final String nameSpace; + public final org.w3c.dom.Document document; + public final String XMLNS_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; + private final Map extraNamespaceMap; + private final Element root; + + public XSDGenerator(XMLUniverse universe, String nameSpace) { + this.universe = universe; + this.nameSpace = nameSpace; + try { + document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } catch (Exception e) { + throw new RuntimeException(e); + } + extraNamespaceMap = new LinkedHashMap<>(); + int nextId = 0; + for (XMLGuiLoader loader : universe.guiElements.values()) { + String namespace = loader.getName().getNamespaceURI(); + if (!extraNamespaceMap.containsKey(namespace)) { + extraNamespaceMap.put(namespace, XMLUniverse.MOULCONFIG_XML_NS.equals(namespace) ? "moulconfig" : "extrans" + nextId++); + } + } + root = document.createElementNS(XMLNS_XML_SCHEMA, "schema"); + root.setPrefix("xs"); + root.setAttribute("targetNamespace", nameSpace); + root.setAttribute("elementFormDefault", "qualified"); + root.setAttribute("xmlns", nameSpace); + for (Map.Entry entry : extraNamespaceMap.entrySet()) { + root.setAttribute("xmlns:" + entry.getValue(), entry.getKey()); + } + document.appendChild(root); + } + + public static void main(String[] args) { + XMLUniverse universe = XMLUniverse.getDefaultUniverse(); + XSDGenerator generator = new XSDGenerator(universe, XMLUniverse.MOULCONFIG_XML_NS); + generator.writeAll(); + generator.dumpToFile(new File("MoulConfig.xsd")); + } + + public String getXMLNS_XML_SCHEMA() { + return XMLNS_XML_SCHEMA; + } + + public void dumpToFile(File file) { + try { + TransformerFactory.newInstance().newTransformer().transform(new DOMSource(document), new StreamResult(file)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void writeAll() { + if (XMLUniverse.MOULCONFIG_XML_NS.equals(nameSpace)) { + writeBaseCases(); + } + for (XMLGuiLoader type : universe.guiElements.values()) { + if (type.getName().getNamespaceURI().equals(nameSpace)) writeType(type); + } + for (XMLGuiLoader type : universe.guiElements.values()) { + if (type.getName().getNamespaceURI().equals(nameSpace)) writeElement(type); + } + } + + public Element createChild(Element base, String nameSpace, String local) { + Element newElement = document.createElementNS(nameSpace, local); + if (XMLNS_XML_SCHEMA.equals(nameSpace)) newElement.setPrefix("xs"); + base.appendChild(newElement); + return newElement; + } + + public void writeBaseCases() { + Element anyWidget = createChild(root, XMLNS_XML_SCHEMA, "element"); + anyWidget.setAttribute("name", "AnyWidget"); + anyWidget.setAttribute("abstract", "true"); + Element widgetLess = createChild(root, XMLNS_XML_SCHEMA, "complexType"); + widgetLess.setAttribute("name", "Widgetless"); + Element singleWidget = createChild(root, XMLNS_XML_SCHEMA, "complexType"); + singleWidget.setAttribute("name", "SingleWidget"); + createChild(createChild(singleWidget, XMLNS_XML_SCHEMA, "sequence"), XMLNS_XML_SCHEMA, "element") + .setAttribute("ref", "moulconfig:AnyWidget"); + + Element multiWidget = createChild(root, XMLNS_XML_SCHEMA, "complexType"); + multiWidget.setAttribute("name", "MultiWidget"); + Element multiSequence = createChild(multiWidget, XMLNS_XML_SCHEMA, "sequence"); + createChild(multiSequence, XMLNS_XML_SCHEMA, "element").setAttribute("ref", "moulconfig:AnyWidget"); + multiSequence.setAttribute("minOccurs", "0"); + multiSequence.setAttribute("maxOccurs", "unbounded"); + + Element twoWidget = createChild(root, XMLNS_XML_SCHEMA, "complexType"); + twoWidget.setAttribute("name", "TwoWidget"); + Element twoSequence = createChild(twoWidget, XMLNS_XML_SCHEMA, "sequence"); + createChild(twoSequence, XMLNS_XML_SCHEMA, "element").setAttribute("ref", "moulconfig:AnyWidget"); + twoSequence.setAttribute("minOccurs", "2"); + twoSequence.setAttribute("maxOccurs", "2"); + } + + public void writeType(XMLGuiLoader type) { + type.emitXSDType(this, root); + } + + public void writeElement(XMLGuiLoader type) { + Element typeNode = createChild(root, XMLNS_XML_SCHEMA, "element"); + typeNode.setAttribute("name", type.getName().getLocalPart()); + typeNode.setAttribute("type", type.getName().getLocalPart()); + typeNode.setAttribute("substitutionGroup", "moulconfig:AnyWidget"); + } + + public Element emitBasicType(XMLGuiLoader.Basic type) { + Element typeNode = createChild(root, XMLNS_XML_SCHEMA, "complexType"); + typeNode.setAttribute("name", type.getName().getLocalPart()); + Element complexContent = createChild(typeNode, XMLNS_XML_SCHEMA, "complexContent"); + Element extension = createChild(complexContent, XMLNS_XML_SCHEMA, "extension"); + String base; + switch (type.getChildCount()) { + case NONE: base = "moulconfig:Widgetless"; break; + case ONE: base = "moulconfig:SingleWidget"; break; + case ANY: base = "moulconfig:MultiWidget"; break; + case TWO: base = "moulconfig:TwoWidget"; break; + default: throw new IllegalStateException("Unknown child count"); + } + extension.setAttribute("base", base); + for (Map.Entry entry : type.getAttributeNames().entrySet()) { + Element attribute = createChild(extension, XMLNS_XML_SCHEMA, "attribute"); + attribute.setAttribute("name", entry.getKey()); + if (entry.getValue()) attribute.setAttribute("use", "required"); + } + return typeNode; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.kt deleted file mode 100644 index 336ea7027..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/XSDGenerator.kt +++ /dev/null @@ -1,156 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml - -import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent -import org.w3c.dom.Element -import java.io.File -import javax.xml.namespace.QName -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.transform.TransformerFactory -import javax.xml.transform.dom.DOMSource -import javax.xml.transform.stream.StreamResult - -class XSDGenerator(val universe: XMLUniverse, val nameSpace: String) { - val document = DocumentBuilderFactory.newInstance() - .also { - it.isNamespaceAware = true - } - .newDocumentBuilder() - .newDocument() - val XMLNS_XML_SCHEMA: String = "http://www.w3.org/2001/XMLSchema" - val extraNamespaceMap = run { - var nextId = 0 - universe.guiElements.values.mapTo(mutableSetOf()) { it.name.namespaceURI }.associate { - if (it == XMLUniverse.MOULCONFIG_XML_NS) it to "moulconfig" - else it to "extrans${nextId++}" - } - } - val root: Element = document.createElementNS(XMLNS_XML_SCHEMA, "schema") - .also { - it.prefix = "xs" - it.setAttribute( - "targetNamespace", - nameSpace - ) - it.setAttribute("elementFormDefault", "qualified") - it.setAttribute( - "xmlns", - nameSpace - ) - extraNamespaceMap.forEach { (k, v) -> - it.setAttribute("xmlns:$v", k) - } - document.appendChild(it) - } - - companion object { - @JvmStatic - fun main(args: Array) { - val universe = XMLUniverse.getDefaultUniverse() - run { - val generator = XSDGenerator(universe, XMLUniverse.MOULCONFIG_XML_NS) - generator.writeAll() - generator.dumpToFile(File("MoulConfig.xsd")) - } - } - } - - fun dumpToFile(file: File) { - val trans = TransformerFactory.newInstance().newTransformer() - val source = DOMSource(document) - val outputStream = StreamResult(file.outputStream()) - trans.transform(source, outputStream) - } - - fun writeAll() { - if (this.nameSpace == XMLUniverse.MOULCONFIG_XML_NS) { - writeBaseCases() - } - for (type in universe.guiElements.values) { - if (type.name.namespaceURI == this.nameSpace) - writeType(type) - } - for (type in universe.guiElements.values) { - if (type.name.namespaceURI == this.nameSpace) - writeElement(type) - } - } - - fun createChild(base: Element, nameSpace: String, local: String): Element { - return base.createChild(nameSpace, local) - } - - @JvmName("createChildInternal") - private fun Element.createChild(nameSpace: String, local: String): Element { - val newElement = document.createElementNS(nameSpace, local) - if (nameSpace == XMLNS_XML_SCHEMA) - newElement.prefix = "xs" - - appendChild(newElement) - return newElement - } - - fun writeBaseCases() { - val anyWidget = root.createChild(XMLNS_XML_SCHEMA, "element") - anyWidget.setAttribute("name", "AnyWidget") - anyWidget.setAttribute("abstract", "true") - val widgetLess = root.createChild(XMLNS_XML_SCHEMA, "complexType") - widgetLess.setAttribute("name", "Widgetless") - val singleWidget = root.createChild(XMLNS_XML_SCHEMA, "complexType") - singleWidget.setAttribute("name", "SingleWidget") - singleWidget.createChild(XMLNS_XML_SCHEMA, "sequence").also { - it.createChild(XMLNS_XML_SCHEMA, "element") - .setAttribute("ref", "moulconfig:AnyWidget") - } - - val multiWidget = root.createChild(XMLNS_XML_SCHEMA, "complexType") - multiWidget.setAttribute("name", "MultiWidget") - multiWidget.createChild(XMLNS_XML_SCHEMA, "sequence").also { - it.createChild(XMLNS_XML_SCHEMA, "element") - .setAttribute("ref", "moulconfig:AnyWidget") - it.setAttribute("minOccurs", "0") - it.setAttribute("maxOccurs", "unbounded") - } - - val twoWidget = root.createChild(XMLNS_XML_SCHEMA, "complexType") - twoWidget.setAttribute("name", "TwoWidget") - twoWidget.createChild(XMLNS_XML_SCHEMA, "sequence").also { - it.createChild(XMLNS_XML_SCHEMA, "element") - .setAttribute("ref", "moulconfig:AnyWidget") - it.setAttribute("minOccurs", "2") - it.setAttribute("maxOccurs", "2") - } - } - - fun writeType(type: XMLGuiLoader<*>) { - type.emitXSDType(this, root) - } - - fun writeElement(type: XMLGuiLoader<*>) { - val typeNode = root.createChild(XMLNS_XML_SCHEMA, "element") - typeNode.setAttribute("name", type.name.localPart) - typeNode.setAttribute("type", type.name.localPart) - typeNode.setAttribute("substitutionGroup", "moulconfig:AnyWidget") - } - - fun emitBasicType(type: XMLGuiLoader.Basic<*>): Element { - val typeNode = root.createChild(XMLNS_XML_SCHEMA, "complexType") - typeNode.setAttribute("name", type.name.localPart) - val complexContent = typeNode.createChild(XMLNS_XML_SCHEMA, "complexContent") - val extension = complexContent.createChild(XMLNS_XML_SCHEMA, "extension") - extension.setAttribute( - "base", when (type.childCount) { - ChildCount.NONE -> "moulconfig:Widgetless" - ChildCount.ONE -> "moulconfig:SingleWidget" - ChildCount.ANY -> "moulconfig:MultiWidget" - ChildCount.TWO -> "moulconfig:TwoWidget" - } - ) - type.attributeNames.forEach { name, required -> - val attribute = extension.createChild(XMLNS_XML_SCHEMA, "attribute") - attribute.setAttribute("name", name) - if (required) - attribute.setAttribute("use", "required") - } - return typeNode - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.java new file mode 100644 index 000000000..62cecea17 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.java @@ -0,0 +1,39 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.HorizontalAlign; +import io.github.notenoughupdates.moulconfig.gui.VerticalAlign; +import io.github.notenoughupdates.moulconfig.gui.component.AlignComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class AlignLoader implements XMLGuiLoader.Basic { + @Override + public AlignComponent createInstance(XMLContext context, Element element) { + return new AlignComponent( + context.getChildFragment(element), + valueOr(context.getPropertyFromAttribute(element, new QName("horizontal"), HorizontalAlign.class), HorizontalAlign.LEFT), + valueOr(context.getPropertyFromAttribute(element, new QName("vertical"), VerticalAlign.class), VerticalAlign.TOP) + ); + } + + private static GetSetter valueOr(GetSetter value, T def) { + return value != null ? value : GetSetter.constant(def); + } + + @Override public QName getName() { return XMLUniverse.qName("Align"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("horizontal", false); + map.put("vertical", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.kt deleted file mode 100644 index 23650e530..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/AlignLoader.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.HorizontalAlign -import io.github.notenoughupdates.moulconfig.gui.VerticalAlign -import io.github.notenoughupdates.moulconfig.gui.component.AlignComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class AlignLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): AlignComponent { - return AlignComponent( - context.getChildFragment(element), - context.getPropertyFromAttribute(element, QName("horizontal"), HorizontalAlign::class.java) - ?: GetSetter.constant(HorizontalAlign.LEFT), - context.getPropertyFromAttribute(element, QName("vertical"), VerticalAlign::class.java) - ?: GetSetter.constant(VerticalAlign.TOP), - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Align") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf("horizontal" to false, "vertical" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.java new file mode 100644 index 000000000..7e45e809d --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.java @@ -0,0 +1,41 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.component.ArrayComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.observer.ObservableList; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.awt.Color; +import java.util.LinkedHashMap; +import java.util.Map; + +public class ArrayLoader implements XMLGuiLoader.Basic { + @Override + @SuppressWarnings("unchecked") + public GuiComponent createInstance(XMLContext context, Element element) { + GetSetter list = context.getPropertyFromAttribute(element, new QName("data"), ObservableList.class); + return new ArrayComponent( + list.get(), + item -> context.getChildFragment(element, item), + valueOr(context.getPropertyFromAttribute(element, new QName("oddBackground"), Color.class), new Color(0, true)), + valueOr(context.getPropertyFromAttribute(element, new QName("evenBackground"), Color.class), new Color(0, true)) + ); + } + + private static GetSetter valueOr(GetSetter value, T def) { return value != null ? value : GetSetter.constant(def); } + @Override public QName getName() { return XMLUniverse.qName("Array"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("data", true); + map.put("oddBackground", false); + map.put("evenBackground", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.kt deleted file mode 100644 index 24b9feaa8..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ArrayLoader.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.component.ArrayComponent -import io.github.notenoughupdates.moulconfig.internal.MapOfs -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.observer.ObservableList -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.jetbrains.annotations.Unmodifiable -import org.w3c.dom.Element -import java.awt.Color -import javax.xml.namespace.QName - -class ArrayLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): GuiComponent { - val list = context.getPropertyFromAttribute(element, QName("data"), ObservableList::class.java) - return ArrayComponent( - list!!.get(), - { context.getChildFragment(element, it) }, - context.getPropertyFromAttribute(element, QName("oddBackground"), Color::class.java) - ?: GetSetter.constant(Color(0, true)), - context.getPropertyFromAttribute(element, QName("evenBackground"), Color::class.java) - ?: GetSetter.constant(Color(0, true)), - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Array") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): @Unmodifiable MutableMap { - return MapOfs.of("data", true, "oddBackground", false, "evenBackground", false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.java new file mode 100644 index 000000000..1e299d5f4 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.java @@ -0,0 +1,43 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.component.CollapsibleComponent; +import io.github.notenoughupdates.moulconfig.gui.component.TextComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class BasicCollapsibleLoader implements XMLGuiLoader.Basic { + @Override + public CollapsibleComponent createInstance(XMLContext context, Element element) { + GetSetter state = context.getPropertyFromAttribute(element, new QName("value"), Boolean.class); + if (state == null) state = GetSetter.floating(true); + io.github.notenoughupdates.moulconfig.gui.GuiComponent body = context.getChildFragment(element); + GetSetter title = context.getPropertyFromAttribute(element, new QName("title"), String.class); + TextComponent textComponent = new TextComponent( + IMinecraft.INSTANCE.getDefaultFontRenderer(), + () -> StructuredText.of(title.get()), + IMinecraft.INSTANCE.getDefaultFontRenderer().getStringWidth(title.get()), + TextComponent.TextAlignment.LEFT, + false, + false + ); + return new CollapsibleComponent(() -> textComponent, () -> body, state); + } + @Override public QName getName() { return XMLUniverse.qName("Collapsible"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("title", true); + map.put("value", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.kt deleted file mode 100644 index d820978f1..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/BasicCollapsibleLoader.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.common.IMinecraft -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.gui.component.CollapsibleComponent -import io.github.notenoughupdates.moulconfig.gui.component.TextComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class BasicCollapsibleLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): CollapsibleComponent { - val state = context.getPropertyFromAttribute(element, QName("value"), Boolean::class.java) - ?: GetSetter.floating(true) - val body = context.getChildFragment(element) - val title = context.getPropertyFromAttribute(element, QName("title"), String::class.java)!! - val textComponent = TextComponent( - IMinecraft.INSTANCE.defaultFontRenderer, - { StructuredText.of(title.get()) }, - IMinecraft.INSTANCE.defaultFontRenderer.getStringWidth(title.get()), - TextComponent.TextAlignment.LEFT, - false, - false - ) - return CollapsibleComponent( - { textComponent }, - { body }, - state - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Collapsible") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf("title" to true, "value" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.java new file mode 100644 index 000000000..8a7cdb978 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.java @@ -0,0 +1,19 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.Map; + +public class CenterLoader implements XMLGuiLoader.Basic { + @Override public CenterComponent createInstance(XMLContext context, Element element) { return new CenterComponent(context.getChildFragment(element)); } + @Override public QName getName() { return XMLUniverse.qName("Center"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { return Collections.emptyMap(); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.kt deleted file mode 100644 index 5847784c1..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/CenterLoader.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class CenterLoader : XMLGuiLoader.Basic { - override fun createInstance( - context: XMLContext<*>, - element: Element - ): CenterComponent { - return CenterComponent( - context.getChildFragment( - element - ) - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Center") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf() - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.java new file mode 100644 index 000000000..3073022ca --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.java @@ -0,0 +1,31 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class FragmentLoader implements XMLGuiLoader.Basic { + @Override + public GuiComponent createInstance(XMLContext context, Element element) { + MyResourceLocation location = context.getPropertyFromAttribute(element, new QName("value"), MyResourceLocation.class).get(); + GetSetter bind = context.getPropertyFromAttribute(element, new QName("bind"), Object.class); + return context.getUniverse().load(bind != null ? bind.get() : element, location); + } + @Override public QName getName() { return XMLUniverse.qName("Fragment"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("value", true); + map.put("bind", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.kt deleted file mode 100644 index 6e81e923d..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/FragmentLoader.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.common.MyResourceLocation -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class FragmentLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): GuiComponent { - val location = context.getPropertyFromAttribute(element, QName("value"), MyResourceLocation::class.java)!!.get() - val bind = context.getPropertyFromAttribute(element, QName("bind"), Any::class.java)?.get() ?: element - return context.universe.load(bind, location) - } - - override fun getName(): QName { - return XMLUniverse.qName("Fragment") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("value" to true, "bind" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.java new file mode 100644 index 000000000..18ce9bc05 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.java @@ -0,0 +1,35 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.component.HoverComponent; +import io.github.notenoughupdates.moulconfig.internal.StructuredTextHelper; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class HoverLoader implements XMLGuiLoader.Basic { + @Override + @SuppressWarnings("unchecked") + public HoverComponent createInstance(XMLContext context, Element element) { + GetSetter list = context.getPropertyFromAttribute(element, new QName("lines"), List.class); + return new HoverComponent(context.getChildFragment(element), () -> { + List result = new ArrayList<>(); + for (Object item : list.get()) { + result.add(StructuredTextHelper.mapStringOrStructuredText(item)); + } + return result; + }); + } + @Override public QName getName() { return XMLUniverse.qName("Hover"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { return Collections.singletonMap("lines", true); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.kt deleted file mode 100644 index 793ca4ce3..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/HoverLoader.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.HoverComponent -import io.github.notenoughupdates.moulconfig.internal.StructuredTextHelper -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import java.util.function.Supplier -import javax.xml.namespace.QName - -class HoverLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): HoverComponent { - val list = context.getPropertyFromAttribute(element, QName("lines"), List::class.java)!! - return HoverComponent( - context.getChildFragment(element), - Supplier { - list.get().map { - StructuredTextHelper.mapStringOrStructuredText(it) - } - } - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Hover") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf("lines" to true) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.java new file mode 100644 index 000000000..0127e1d87 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.java @@ -0,0 +1,22 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.component.IndirectComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.Map; + +public class IndirectLoader implements XMLGuiLoader.Basic { + @Override public IndirectComponent createInstance(XMLContext context, Element element) { + return new IndirectComponent(context.getPropertyFromAttribute(element, new QName("value"), GuiComponent.class)); + } + @Override public QName getName() { return XMLUniverse.qName("Indirect"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { return Collections.singletonMap("value", true); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.kt deleted file mode 100644 index a2870b065..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/IndirectLoader.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.gui.component.IndirectComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class IndirectLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): IndirectComponent { - return IndirectComponent( - context.getPropertyFromAttribute( - element, - QName("value"), - GuiComponent::class.java - )!! - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Indirect") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("value" to true) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.java new file mode 100644 index 000000000..81bf7ecb5 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.java @@ -0,0 +1,22 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.common.IItemStack; +import io.github.notenoughupdates.moulconfig.gui.component.ItemStackComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.Map; + +public class ItemStackLoader implements XMLGuiLoader.Basic { + @Override public ItemStackComponent createInstance(XMLContext context, Element element) { + return new ItemStackComponent(context.getPropertyFromAttribute(element, new QName("value"), IItemStack.class)); + } + @Override public QName getName() { return XMLUniverse.qName("ItemStack"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { return Collections.singletonMap("value", true); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.kt deleted file mode 100644 index 5c5281bf3..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ItemStackLoader.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.common.IItemStack -import io.github.notenoughupdates.moulconfig.gui.component.ItemStackComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class ItemStackLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): ItemStackComponent { - return ItemStackComponent( - context.getPropertyFromAttribute(element, QName("value"), IItemStack::class.java)!! - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("ItemStack") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("value" to true) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.java new file mode 100644 index 000000000..0460c53e7 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.java @@ -0,0 +1,32 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.CloseEventListener; +import io.github.notenoughupdates.moulconfig.gui.component.MetaComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class MetaLoader implements XMLGuiLoader.Basic { + @Override public MetaComponent createInstance(XMLContext context, Element element) { + return new MetaComponent( + context.getPropertyFromAttribute(element, new QName("beforeClose"), CloseEventListener.CloseAction.class), + context.getMethodFromAttribute(element, new QName("afterClose")), + context.getPropertyFromAttribute(element, new QName("requestClose"), Runnable.class) + ); + } + @Override public QName getName() { return XMLUniverse.qName("Meta"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("beforeClose", false); + map.put("afterClose", false); + map.put("requestClose", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.kt deleted file mode 100644 index d66b6edba..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/MetaLoader.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.CloseEventListener -import io.github.notenoughupdates.moulconfig.gui.component.MetaComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class MetaLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): MetaComponent { - val beforeClose = - context.getPropertyFromAttribute(element, QName("beforeClose"), CloseEventListener.CloseAction::class.java) - val afterClose = context.getMethodFromAttribute(element, QName("afterClose")) - val requestClose = context.getPropertyFromAttribute(element, QName("requestClose"), Runnable::class.java) - return MetaComponent(beforeClose, afterClose, requestClose) - } - - override fun getName(): QName { - return XMLUniverse.qName("Meta") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("beforeClose" to false, "afterClose" to false, "requestClose" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.java new file mode 100644 index 000000000..9798f3a04 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.java @@ -0,0 +1,30 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class PanelLoader implements XMLGuiLoader.Basic { + @Override public PanelComponent createInstance(XMLContext context, Element element) { + return new PanelComponent( + context.getChildFragment(element), + context.getPropertyFromAttribute(element, new QName("insets"), Integer.class, 2), + context.getPropertyFromAttribute(element, new QName("background"), PanelComponent.BackgroundRenderer.class, PanelComponent.DefaultBackgroundRenderer.DARK_RECT) + ); + } + @Override public QName getName() { return XMLUniverse.qName("Panel"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("insets", false); + map.put("background", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.kt deleted file mode 100644 index 1facc3f1e..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/PanelLoader.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent -import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent.DefaultBackgroundRenderer -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class PanelLoader : XMLGuiLoader.Basic { - override fun createInstance( - context: XMLContext<*>, - element: Element - ): PanelComponent { - return PanelComponent( - context.getChildFragment( - element - ), - context.getPropertyFromAttribute(element, QName("insets"), Int::class.java, 2), - context.getPropertyFromAttribute( - element, - QName("background"), - PanelComponent.BackgroundRenderer::class.java, - DefaultBackgroundRenderer.DARK_RECT - ) - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Panel") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf("insets" to false, "background" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.java new file mode 100644 index 000000000..b719a5225 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.java @@ -0,0 +1,19 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.Map; + +public class RootLoader implements XMLGuiLoader.Basic { + @Override public GuiComponent createInstance(XMLContext context, Element element) { return context.getChildFragment(element); } + @Override public QName getName() { return XMLUniverse.qName("Root"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { return Collections.emptyMap(); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.kt deleted file mode 100644 index 2aab69733..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/RootLoader.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.GuiComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class RootLoader : XMLGuiLoader.Basic { - override fun createInstance( - context: XMLContext<*>, - element: Element - ): GuiComponent { - return context.getChildFragment(element) - } - - override fun getName(): QName { - return XMLUniverse.qName("Root") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf() - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.java new file mode 100644 index 000000000..230feaa4e --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.java @@ -0,0 +1,21 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.ScaleComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.Map; + +public class ScaleLoader implements XMLGuiLoader.Basic { + @Override public ScaleComponent createInstance(XMLContext context, Element element) { + return new ScaleComponent(context.getChildFragment(element), context.getPropertyFromAttribute(element, new QName("scale"), Float.class)); + } + @Override public QName getName() { return XMLUniverse.qName("Scale"); } + @Override public ChildCount getChildCount() { return ChildCount.ONE; } + @Override public Map getAttributeNames() { return Collections.singletonMap("scale", true); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.kt deleted file mode 100644 index 1e9d64135..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/ScaleLoader.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.ScaleComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class ScaleLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): ScaleComponent { - return ScaleComponent( - context.getChildFragment(element), - context.getPropertyFromAttribute(element, QName("scale"), Float::class.java)!! - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Scale") - } - - override fun getChildCount(): ChildCount { - return ChildCount.ONE - } - - override fun getAttributeNames(): Map { - return mapOf("scale" to true) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.java new file mode 100644 index 000000000..6da764263 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.java @@ -0,0 +1,29 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.SpacerComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class SpacerLoader implements XMLGuiLoader.Basic { + @Override public SpacerComponent createInstance(XMLContext context, Element element) { + GetSetter width = context.getPropertyFromAttribute(element, new QName("width"), Integer.class); + GetSetter height = context.getPropertyFromAttribute(element, new QName("height"), Integer.class); + return new SpacerComponent(width != null ? width : GetSetter.constant(0), height != null ? height : GetSetter.constant(0)); + } + @Override public QName getName() { return XMLUniverse.qName("Spacer"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("width", false); + map.put("height", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.kt deleted file mode 100644 index 861f11331..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/SpacerLoader.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.SpacerComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class SpacerLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): SpacerComponent { - return SpacerComponent( - context.getPropertyFromAttribute(element, QName("width"), Int::class.java) ?: GetSetter.constant(0), - context.getPropertyFromAttribute(element, QName("height"), Int::class.java) ?: GetSetter.constant(0), - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Spacer") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("width" to false, "height" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.java new file mode 100644 index 000000000..c68edae07 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.java @@ -0,0 +1,68 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.TabComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import io.github.notenoughupdates.moulconfig.xml.XSDGenerator; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.List; + +public class TabsLoader implements XMLGuiLoader { + @Override + public TabComponent createInstance(XMLContext context, Element element) { + NodeList tabElements = element.getElementsByTagName("Tab"); + List tabs = new ArrayList<>(); + for (int i = 0; i < tabElements.getLength(); i++) { + Element tabElement = (Element) tabElements.item(i); + Element body = (Element) tabElement.getElementsByTagName("Tab.Body").item(0); + Element header = (Element) tabElement.getElementsByTagName("Tab.Header").item(0); + tabs.add(new TabComponent.Tab(context.getChildFragment(header), context.getChildFragment(body))); + } + GetSetter selectedTabIndex = context.getPropertyFromAttribute(element, new QName("selectedTabIndex"), Integer.class); + if (selectedTabIndex == null) { + selectedTabIndex = GetSetter.floating(context.getPropertyFromAttribute(element, new QName("initialSelectedTabIndex"), Integer.class, 0)); + } + return new TabComponent(tabs, selectedTabIndex); + } + + @Override + public QName getName() { + return XMLUniverse.qName("Tabs"); + } + + @Override + public Element emitXSDType(XSDGenerator generator, Element root) { + Element typeNode = generator.createChild(root, generator.XMLNS_XML_SCHEMA, "complexType"); + typeNode.setAttribute("name", getName().getLocalPart()); + Element complexContent = generator.createChild(typeNode, generator.XMLNS_XML_SCHEMA, "complexContent"); + Element extension = generator.createChild(complexContent, generator.XMLNS_XML_SCHEMA, "extension"); + extension.setAttribute("base", "Tabs.Content"); + Element attributeTab = generator.createChild(extension, generator.XMLNS_XML_SCHEMA, "attribute"); + attributeTab.setAttribute("name", "selectedTabIndex"); + Element attributeDefaultTab = generator.createChild(extension, generator.XMLNS_XML_SCHEMA, "attribute"); + attributeDefaultTab.setAttribute("name", "initialSelectedTabIndex"); + + Element childTypeNode = generator.createChild(root, generator.XMLNS_XML_SCHEMA, "complexType"); + childTypeNode.setAttribute("name", "Tabs.Content"); + Element sequence = generator.createChild(childTypeNode, generator.XMLNS_XML_SCHEMA, "sequence"); + sequence.setAttribute("maxOccurs", "unbounded"); + Element sequenceElement = generator.createChild(sequence, generator.XMLNS_XML_SCHEMA, "element"); + sequenceElement.setAttribute("name", "Tab"); + + Element tabType = generator.createChild(sequenceElement, generator.XMLNS_XML_SCHEMA, "complexType"); + Element tabSequence = generator.createChild(tabType, generator.XMLNS_XML_SCHEMA, "sequence"); + Element tabHeader = generator.createChild(tabSequence, generator.XMLNS_XML_SCHEMA, "element"); + tabHeader.setAttribute("name", "Tab.Header"); + tabHeader.setAttribute("type", "SingleWidget"); + Element tabBody = generator.createChild(tabSequence, generator.XMLNS_XML_SCHEMA, "element"); + tabBody.setAttribute("name", "Tab.Body"); + tabBody.setAttribute("type", "SingleWidget"); + return typeNode; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.kt deleted file mode 100644 index 4490561eb..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TabsLoader.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.TabComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import io.github.notenoughupdates.moulconfig.xml.XSDGenerator -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class TabsLoader : XMLGuiLoader { - override fun createInstance(context: XMLContext<*>, element: Element): TabComponent { - val tabElements = element.getElementsByTagName("Tab") - val tabs = mutableListOf() - for (i in 0 until tabElements.length) { - val tabElement = tabElements.item(i) as Element - val body = tabElement.getElementsByTagName("Tab.Body").item(0) as Element - val header = tabElement.getElementsByTagName("Tab.Header").item(0) as Element - tabs.add(TabComponent.Tab( - context.getChildFragment(header), - context.getChildFragment(body), - )) - } - return TabComponent( - tabs, - context.getPropertyFromAttribute(element, QName("selectedTabIndex"), Int::class.java) - ?: GetSetter.floating(context.getPropertyFromAttribute(element, QName("initialSelectedTabIndex"), Int::class.java, 0)) - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("Tabs") - } - - override fun emitXSDType(generator: XSDGenerator, root: Element): Element { - val typeNode = generator.createChild(root, generator.XMLNS_XML_SCHEMA, "complexType") - typeNode.setAttribute("name", name.localPart) - val complexContent = generator.createChild(typeNode, generator.XMLNS_XML_SCHEMA, "complexContent") - val extension = generator.createChild(complexContent, generator.XMLNS_XML_SCHEMA, "extension") - extension.setAttribute("base", "Tabs.Content") - val attributeTab = generator.createChild(extension, generator.XMLNS_XML_SCHEMA, "attribute") - attributeTab.setAttribute("name", "selectedTabIndex") - val attributeDefaultTab = generator.createChild(extension, generator.XMLNS_XML_SCHEMA, "attribute") - attributeDefaultTab.setAttribute("name", "initialSelectedTabIndex") - - val childTypeNode = generator.createChild(root, generator.XMLNS_XML_SCHEMA, "complexType") - childTypeNode.setAttribute("name", "Tabs.Content") - val sequence = generator.createChild(childTypeNode, generator.XMLNS_XML_SCHEMA, "sequence") - sequence.setAttribute("maxOccurs", "unbounded") - val sequenceElement = generator.createChild(sequence, generator.XMLNS_XML_SCHEMA, "element") - sequenceElement.setAttribute("name", "Tab") - - val tabType = generator.createChild(sequenceElement, generator.XMLNS_XML_SCHEMA, "complexType") - val tabSequence = generator.createChild(tabType, generator.XMLNS_XML_SCHEMA, "sequence") - val tabHeader = generator.createChild(tabSequence, generator.XMLNS_XML_SCHEMA, "element") - tabHeader.setAttribute("name", "Tab.Header") - tabHeader.setAttribute("type", "SingleWidget") - val tabBody = generator.createChild(tabSequence, generator.XMLNS_XML_SCHEMA, "element") - tabBody.setAttribute("name", "Tab.Body") - tabBody.setAttribute("type", "SingleWidget") - return typeNode - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.java new file mode 100644 index 000000000..d723056c1 --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.java @@ -0,0 +1,36 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.LinkedHashMap; +import java.util.Map; + +public class TextFieldLoader implements XMLGuiLoader.Basic { + @Override public TextFieldComponent createInstance(XMLContext context, Element element) { + GetSetter editable = context.getPropertyFromAttribute(element, new QName("editable"), Boolean.class); + if (editable == null) editable = GetSetter.constant(true); + return new TextFieldComponent( + context.getPropertyFromAttribute(element, new QName("value"), String.class), + context.getPropertyFromAttribute(element, new QName("width"), Integer.class, 80), + editable, + context.getPropertyFromAttribute(element, new QName("suggestion"), String.class, "") + ); + } + @Override public QName getName() { return XMLUniverse.qName("TextField"); } + @Override public ChildCount getChildCount() { return ChildCount.NONE; } + @Override public Map getAttributeNames() { + Map map = new LinkedHashMap<>(); + map.put("value", true); + map.put("width", false); + map.put("editable", false); + map.put("suggestion", false); + return map; + } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.kt deleted file mode 100644 index fa74ba41e..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/TextFieldLoader.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent -import io.github.notenoughupdates.moulconfig.observer.GetSetter -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class TextFieldLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): TextFieldComponent { - return TextFieldComponent( - context.getPropertyFromAttribute(element, QName("value"), String::class.java)!!, - context.getPropertyFromAttribute(element, QName("width"), Int::class.java, 80), - context.getPropertyFromAttribute(element, QName("editable"), Boolean::class.java) - ?: GetSetter.constant(true), - context.getPropertyFromAttribute(element, QName("suggestion"), String::class.java, ""), - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("TextField") - } - - override fun getChildCount(): ChildCount { - return ChildCount.NONE - } - - override fun getAttributeNames(): Map { - return mapOf("value" to true, "width" to false, "editable" to false, "suggestion" to false) - } -} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.java b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.java new file mode 100644 index 000000000..4642cdc1c --- /dev/null +++ b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.java @@ -0,0 +1,29 @@ +package io.github.notenoughupdates.moulconfig.xml.loaders; + +import io.github.notenoughupdates.moulconfig.gui.GuiComponent; +import io.github.notenoughupdates.moulconfig.gui.component.WhenComponent; +import io.github.notenoughupdates.moulconfig.xml.ChildCount; +import io.github.notenoughupdates.moulconfig.xml.XMLContext; +import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class WhenLoader implements XMLGuiLoader.Basic { + @Override public WhenComponent createInstance(XMLContext context, Element element) { + List fragments = context.getChildFragments(element); + if (fragments.size() != 2) throw new IllegalArgumentException("When requires exactly two child fragments"); + return new WhenComponent( + context.getPropertyFromAttribute(element, new QName("condition"), Boolean.class), + () -> fragments.get(0), + () -> fragments.get(1) + ); + } + @Override public QName getName() { return XMLUniverse.qName("When"); } + @Override public ChildCount getChildCount() { return ChildCount.TWO; } + @Override public Map getAttributeNames() { return Collections.singletonMap("condition", true); } +} diff --git a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.kt b/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.kt deleted file mode 100644 index e3cdc0bf1..000000000 --- a/common/src/main/java/io/github/notenoughupdates/moulconfig/xml/loaders/WhenLoader.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.notenoughupdates.moulconfig.xml.loaders - -import io.github.notenoughupdates.moulconfig.gui.component.WhenComponent -import io.github.notenoughupdates.moulconfig.xml.ChildCount -import io.github.notenoughupdates.moulconfig.xml.XMLContext -import io.github.notenoughupdates.moulconfig.xml.XMLGuiLoader -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import org.w3c.dom.Element -import javax.xml.namespace.QName - -class WhenLoader : XMLGuiLoader.Basic { - override fun createInstance(context: XMLContext<*>, element: Element): WhenComponent { - val fragments = context.getChildFragments(element) - require(fragments.size == 2) - return WhenComponent( - context.getPropertyFromAttribute(element, QName("condition"), Boolean::class.java)!!, - { fragments[0] }, - { fragments[1] }, - ) - } - - override fun getName(): QName { - return XMLUniverse.qName("When") - } - - override fun getChildCount(): ChildCount { - return ChildCount.TWO - } - - override fun getAttributeNames(): Map { - return mapOf("condition" to true) - } -} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.java b/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.java new file mode 100644 index 000000000..f9283096e --- /dev/null +++ b/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.java @@ -0,0 +1,31 @@ +package io.github.notenoughupdates.moulconfig; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.awt.Color; + +public class ChromaColourTest { + void testWithSpeed(Color color, int speed) { + String stringRepr = speed + ":" + color.getAlpha() + ":" + color.getRed() + ":" + color.getGreen() + ":" + color.getBlue(); + int time = speed > 0 ? (int) (ChromaColour.getSecondsForSpeed(speed) * 1000) : 0; + ChromaColour fromStatic = ChromaColour.fromRGB(color.getRed(), color.getGreen(), color.getBlue(), time, color.getAlpha()); + if (speed == 0) { + Assertions.assertEquals(color.getRGB(), fromStatic.getEffectiveColourRGB()); + } + Assertions.assertEquals(stringRepr, fromStatic.toLegacyString()); + Assertions.assertEquals(fromStatic, ChromaColour.forLegacyString(stringRepr)); + } + + @Disabled("This test takes around ~8 seconds to run, we don't need that overhead") + @Test + void testAllColours() { + for (int i = 0; i <= 0xFFFFFF; i++) { + Color color = new Color(i); + testWithSpeed(color, 0); + testWithSpeed(color, 10); + testWithSpeed(color, 255); + } + } +} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.kt b/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.kt deleted file mode 100644 index b7e502ea2..000000000 --- a/common/src/test/java/io/github/notenoughupdates/moulconfig/ChromaColourTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.notenoughupdates.moulconfig - -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import java.awt.Color - -/** - * Test to verfiy that [ChromaColour.toLegacyString] and [ChromaColour.forLegacyString] both work as expected, serializing the RGB value. - */ -class ChromaColourTest { - fun testWithSpeed(jColor: Color, speed: Int) { - val stringRepr = "$speed:${jColor.alpha}:${jColor.red}:${jColor.green}:${jColor.blue}" - val time = if (speed > 0) (ChromaColour.getSecondsForSpeed(speed) * 1000).toInt() else 0 - val fromStatic = ChromaColour.fromRGB(jColor.red, jColor.green, jColor.blue, time, jColor.alpha) - if (speed == 0) // Can't test colour accuracy given that the system time influences this - Assertions.assertEquals(jColor.rgb, fromStatic.getEffectiveColourRGB()) - Assertions.assertEquals(stringRepr, fromStatic.toLegacyString()) - Assertions.assertEquals(fromStatic, ChromaColour.forLegacyString(stringRepr)) - } - - @Disabled("This test takes around ~8 seconds to run, we don't need that overhead") - @Test - fun testAllColours() { - yieldAllColours() - .forEach { - testWithSpeed(it, 0) - testWithSpeed(it, 10) - testWithSpeed(it, 255) - } - } - - fun yieldAllColours() = (0..0xFFFFFF) - .asSequence() - .map { Color(it) } -} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.java b/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.java new file mode 100644 index 000000000..627bbe009 --- /dev/null +++ b/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.java @@ -0,0 +1,187 @@ +package io.github.notenoughupdates.moulconfig; + +import io.github.notenoughupdates.moulconfig.annotations.ConfigOrder; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOverride; +import io.github.notenoughupdates.moulconfig.internal.MCLogger; +import io.github.notenoughupdates.moulconfig.internal.Warnings; +import io.github.notenoughupdates.moulconfig.processor.ConfigProcessorDriver; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ConfigFieldOrderTest { + public static class SingleClass { + public Object a = new Object(); + public Object b = new Object(); + public Object c = new Object(); + } + + public static class OrderedSingleClass { + @ConfigOrder(3) public Object c = new Object(); + @ConfigOrder(1) public Object a = new Object(); + @ConfigOrder(2) public Object b = new Object(); + } + + public static class Parent { + public Object x = new Object(); + public Object y = new Object(); + public Object z = new Object(); + } + + public static class ChildAppendsField extends Parent { + public Object w = new Object(); + } + + public static class ChildShadowsWithoutAnnotation extends Parent { + public Object y = new Object(); + } + + public static class ChildShadowsWithAnnotation extends Parent { + @ConfigOverride public Object y = new Object(); + } + + public static class ParentWithOrderedField { + public Object a = new Object(); + @ConfigOrder(5) public Object b = new Object(); + public Object c = new Object(); + } + + public static class ChildInheritsParentOrder extends ParentWithOrderedField { + @ConfigOverride public Object b = new Object(); + } + + public static class ChildExplicitOverrideOrder extends ParentWithOrderedField { + @ConfigOverride(overrideOrder = 99) public Object b = new Object(); + } + + public static class GrandParent { + public Object p = new Object(); + public Object q = new Object(); + } + + public static class MiddleParent extends GrandParent { + public Object r = new Object(); + } + + public static class GrandChild extends MiddleParent { + @ConfigOverride public Object q = new Object(); + } + + private final Method getSortedFields; + private final List capturedWarnings = new ArrayList<>(); + private MCLogger previousLogger; + + public ConfigFieldOrderTest() throws NoSuchMethodException { + getSortedFields = ConfigProcessorDriver.class.getDeclaredMethod("getSortedFields", Class.class); + getSortedFields.setAccessible(true); + } + + @BeforeEach + void installCapturingLogger() { + previousLogger = Warnings.logger; + Warnings.shouldWarn = true; + Warnings.logger = new MCLogger() { + @Override public void warn(String text) { capturedWarnings.add(text); } + @Override public void info(String text) {} + @Override public void error(String text, Throwable throwable) {} + }; + } + + @AfterEach + void restoreLogger() { + Warnings.logger = previousLogger; + capturedWarnings.clear(); + } + + @SuppressWarnings("unchecked") + private List sortedFields(Class type) { + try { + return (List) getSortedFields.invoke(null, type); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private List fieldNames(Class type) { + List names = new ArrayList<>(); + for (Field field : sortedFields(type)) { + names.add(field.getName()); + } + return names; + } + + @Test + void declarationOrderIsPreservedWithoutAnnotations() { + Assertions.assertEquals(Arrays.asList("a", "b", "c"), fieldNames(SingleClass.class)); + } + + @Test + void configOrderSortsFieldsWithinAClass() { + Assertions.assertEquals(Arrays.asList("a", "b", "c"), fieldNames(OrderedSingleClass.class)); + } + + @Test + void parentFieldsAppearBeforeChildFields() { + Assertions.assertEquals(Arrays.asList("x", "y", "z", "w"), fieldNames(ChildAppendsField.class)); + } + + @Test + void shadowingWithoutConfigOverrideEmitsAWarning() { + sortedFields(ChildShadowsWithoutAnnotation.class); + Assertions.assertTrue(capturedWarnings.stream().anyMatch(it -> it.contains("y"))); + } + + @Test + void configOverrideSuppressesShadowWarning() { + sortedFields(ChildShadowsWithAnnotation.class); + Assertions.assertTrue(capturedWarnings.stream().noneMatch(it -> it.contains("y"))); + } + + @Test + void configOverrideSlotsChildFieldIntoParentPosition() { + List fields = sortedFields(ChildShadowsWithAnnotation.class); + List names = new ArrayList<>(); + for (Field field : fields) names.add(field.getName()); + Assertions.assertEquals(Arrays.asList("x", "y", "z"), names); + Assertions.assertEquals(ChildShadowsWithAnnotation.class, fields.get(1).getDeclaringClass()); + } + + @Test + void configOverrideWithoutOverrideOrderInheritsParentConfigOrderValue() { + List fields = sortedFields(ChildInheritsParentOrder.class); + List names = new ArrayList<>(); + Field b = null; + for (Field field : fields) { + names.add(field.getName()); + if (field.getName().equals("b")) b = field; + } + Assertions.assertEquals(Arrays.asList("a", "c", "b"), names); + Assertions.assertEquals(ChildInheritsParentOrder.class, b.getDeclaringClass()); + } + + @Test + void configOverrideWithExplicitOverrideOrderUsesThatValueOverInherited() { + List names = fieldNames(ChildExplicitOverrideOrder.class); + Assertions.assertEquals("b", names.get(names.size() - 1)); + } + + @Test + void overrideWorksCorrectlyThroughMultipleInheritanceLevels() { + List fields = sortedFields(GrandChild.class); + List names = new ArrayList<>(); + Field q = null; + for (Field field : fields) { + names.add(field.getName()); + if (field.getName().equals("q")) q = field; + } + Assertions.assertEquals(Arrays.asList("p", "q", "r"), names); + Assertions.assertEquals(GrandChild.class, q.getDeclaringClass()); + } +} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.kt b/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.kt deleted file mode 100644 index ac234ac19..000000000 --- a/common/src/test/java/io/github/notenoughupdates/moulconfig/ConfigFieldOrderTest.kt +++ /dev/null @@ -1,170 +0,0 @@ -package io.github.notenoughupdates.moulconfig - -import io.github.notenoughupdates.moulconfig.annotations.ConfigOrder -import io.github.notenoughupdates.moulconfig.annotations.ConfigOverride -import io.github.notenoughupdates.moulconfig.internal.MCLogger -import io.github.notenoughupdates.moulconfig.internal.Warnings -import io.github.notenoughupdates.moulconfig.processor.ConfigProcessorDriver -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import java.lang.reflect.Field - -/** - * Tests for field ordering and override deduplication in [ConfigProcessorDriver.getSortedFields]. - * - * Covers: - * - Declaration order is preserved without annotations - * - [ConfigOrder] sorts fields within and across classes - * - Shadowed fields without [ConfigOverride] emit a warning - * - [ConfigOverride] suppresses that warning and slots into the parent's position - * - [ConfigOverride] inherits the parent's [ConfigOrder] value by default - * - [ConfigOverride.overrideOrder] takes precedence over inherited order when set - * - Correct behavior through multiple levels of inheritance - */ -@Suppress("unused") -class ConfigFieldOrderTest { - - open class SingleClass { - val a = Unit - val b = Unit - val c = Unit - } - - open class OrderedSingleClass { - @ConfigOrder(3) - val c = Unit - @ConfigOrder(1) - val a = Unit - @ConfigOrder(2) - val b = Unit - } - - open class Parent { - val x = Unit - open val y = Unit - val z = Unit - } - - open class ChildAppendsField : Parent() { - val w = Unit - } - - open class ChildShadowsWithoutAnnotation : Parent() { - override val y = Unit - } - - open class ChildShadowsWithAnnotation : Parent() { - @ConfigOverride - override val y = Unit - } - - open class ParentWithOrderedField { - val a = Unit - @ConfigOrder(5) - open val b = Unit - val c = Unit - } - - open class ChildInheritsParentOrder : ParentWithOrderedField() { - @ConfigOverride - override val b = Unit - } - - open class ChildExplicitOverrideOrder : ParentWithOrderedField() { - @ConfigOverride(overrideOrder = 99) - override val b = Unit - } - - open class GrandParent { - val p = Unit - open val q = Unit - } - - open class MiddleParent : GrandParent() { - val r = Unit - } - - open class GrandChild : MiddleParent() { - @ConfigOverride - override val q = Unit - } - - private val getSortedFields = ConfigProcessorDriver::class.java - .getDeclaredMethod("getSortedFields", Class::class.java) - .also { it.isAccessible = true } - - private val capturedWarnings = mutableListOf() - private var previousLogger: MCLogger? = null - - @BeforeEach - fun installCapturingLogger() { - previousLogger = Warnings.logger - Warnings.shouldWarn = true - Warnings.logger = object : MCLogger { - override fun warn(text: String) { capturedWarnings.add(text) } - override fun info(text: String) = Unit - override fun error(text: String, throwable: Throwable) = Unit - } - } - - @AfterEach - fun restoreLogger() { - Warnings.logger = previousLogger - capturedWarnings.clear() - } - - @Suppress("UNCHECKED_CAST") - private fun sortedFields(type: Class<*>) = getSortedFields.invoke(null, type) as List - private fun fieldNames(type: Class<*>) = sortedFields(type).map { it.name } - - /** Declaration order of fields within a single class should be preserved when no ordering annotations are present. */ - @Test fun `declaration order is preserved without annotations`() = - assertEquals(listOf("a", "b", "c"), fieldNames(SingleClass::class.java)) - - /** [ConfigOrder] should sort fields by ascending value, regardless of declaration order. */ - @Test fun `ConfigOrder sorts fields within a class`() = - assertEquals(listOf("a", "b", "c"), fieldNames(OrderedSingleClass::class.java)) - - /** Fields declared in a parent class should appear before fields declared in the child. */ - @Test fun `parent fields appear before child fields`() = - assertEquals(listOf("x", "y", "z", "w"), fieldNames(ChildAppendsField::class.java)) - - /** Shadowing a parent field without [ConfigOverride] should emit a warning. */ - @Test fun `shadowing without ConfigOverride emits a warning`() { - sortedFields(ChildShadowsWithoutAnnotation::class.java) - assertTrue(capturedWarnings.any { "y" in it }) - } - - /** [ConfigOverride] should suppress the shadow warning. */ - @Test fun `ConfigOverride suppresses shadow warning`() { - sortedFields(ChildShadowsWithAnnotation::class.java) - assertTrue(capturedWarnings.none { "y" in it }) - } - - /** [ConfigOverride] should slot the child field back into the parent field's original position. */ - @Test fun `ConfigOverride slots child field into parent position`() { - val fields = sortedFields(ChildShadowsWithAnnotation::class.java) - assertEquals(listOf("x", "y", "z"), fields.map { it.name }) - assertEquals(ChildShadowsWithAnnotation::class.java, fields[1].declaringClass) - } - - /** [ConfigOverride] without an explicit [ConfigOverride.overrideOrder] should inherit the parent's [ConfigOrder] value. */ - @Test fun `ConfigOverride without overrideOrder inherits parent ConfigOrder value`() { - val fields = sortedFields(ChildInheritsParentOrder::class.java) - assertEquals(listOf("a", "c", "b"), fields.map { it.name }) - assertEquals(ChildInheritsParentOrder::class.java, fields.first { it.name == "b" }.declaringClass) - } - - /** An explicit [ConfigOverride.overrideOrder] value should take precedence over the inherited parent order. */ - @Test fun `ConfigOverride with explicit overrideOrder uses that value over inherited`() = - assertEquals("b", fieldNames(ChildExplicitOverrideOrder::class.java).last()) - - /** [ConfigOverride] should correctly resolve position through multiple levels of inheritance. */ - @Test fun `override works correctly through multiple inheritance levels`() { - val fields = sortedFields(GrandChild::class.java) - assertEquals(listOf("p", "q", "r"), fields.map { it.name }) - assertEquals(GrandChild::class.java, fields.first { it.name == "q" }.declaringClass) - } -} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.java b/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.java new file mode 100644 index 000000000..83baefb1a --- /dev/null +++ b/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.java @@ -0,0 +1,44 @@ +package io.github.notenoughupdates.moulconfig.common; + +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiContext; +import io.github.notenoughupdates.moulconfig.internal.MCLogger; +import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; + +import java.awt.image.BufferedImage; +import java.io.InputStream; + +public class MockIMinecraft implements IMinecraft { + @Override public boolean isDevelopmentEnvironment() { return false; } + @Override public MCLogger getLogger(String label) { + return new MCLogger() { + @Override public void warn(String text) {} + @Override public void info(String text) {} + @Override public void error(String text, Throwable throwable) {} + }; + } + @Override public InputStream loadResourceLocation(MyResourceLocation resourceLocation) { throw new UnsupportedOperationException(); } + @Override public boolean isGeneratedSentinel(MyResourceLocation resourceLocation) { throw new UnsupportedOperationException(); } + @Override public DynamicTextureReference generateDynamicTexture(BufferedImage image) { throw new UnsupportedOperationException(); } + @Override public MoulConfigPair getMousePositionHF() { throw new UnsupportedOperationException(); } + @Override public IFontRenderer getDefaultFontRenderer() { throw new UnsupportedOperationException(); } + @Override public IKeyboardConstants getKeyboardConstants() { throw new UnsupportedOperationException(); } + @Override public int getScaledWidth() { throw new UnsupportedOperationException(); } + @Override public int getScaledHeight() { throw new UnsupportedOperationException(); } + @Override public int getScaleFactor() { throw new UnsupportedOperationException(); } + @Override public boolean isOnMacOs() { throw new UnsupportedOperationException(); } + @Override public boolean isMouseButtonDown(int mouseButton) { throw new UnsupportedOperationException(); } + @Override public boolean isKeyboardKeyDown(int keyCode) { throw new UnsupportedOperationException(); } + @Override public void addExtraBuiltinConfigProcessors(MoulConfigProcessor processor) { throw new UnsupportedOperationException(); } + @Override public void sendClickableChatMessage(StructuredText message, String action, ClickType clickType) { throw new UnsupportedOperationException(); } + @Override public StructuredText getKeyName(int keyCode) { throw new UnsupportedOperationException(); } + @Override public StructuredText.Mutable createLiteral(String text) { throw new UnsupportedOperationException(); } + @Override public StructuredText.Mutable createTranslatable(String key, StructuredText... args) { throw new UnsupportedOperationException(); } + @Override public StructuredText createStructuredTextInternal(Object object) { throw new UnsupportedOperationException(); } + @Override public void registerPlatformTypeMorphisms(XMLUniverse universe) { throw new UnsupportedOperationException(); } + @Override public RenderContext provideTopLevelRenderContext() { throw new UnsupportedOperationException(); } + @Override public void openWrappedScreen(GuiContext guiContext) { throw new UnsupportedOperationException(); } + @Override public void copyToClipboard(String string) { throw new UnsupportedOperationException(); } + @Override public String copyFromClipboard() { throw new UnsupportedOperationException(); } +} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.kt b/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.kt deleted file mode 100644 index 1659cb7da..000000000 --- a/common/src/test/java/io/github/notenoughupdates/moulconfig/common/MockIMinecraft.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.notenoughupdates.moulconfig.common - -import io.github.notenoughupdates.moulconfig.internal.MCLogger - -/** - * Minimal [IMinecraft] implementation for use in unit tests. - * Only implements what is required for [io.github.notenoughupdates.moulconfig.internal.Warnings] to initialise. - * All other methods throw [UnsupportedOperationException]. - */ -class MockIMinecraft : IMinecraft { - - override fun isDevelopmentEnvironment() = false - - override fun getLogger(label: String) = object : MCLogger { - override fun warn(text: String) = Unit - override fun info(text: String) = Unit - override fun error(text: String, throwable: Throwable) = Unit - } - - override fun loadResourceLocation(resourceLocation: MyResourceLocation) = TODO() - override fun isGeneratedSentinel(resourceLocation: MyResourceLocation) = TODO() - override fun generateDynamicTexture(image: java.awt.image.BufferedImage) = TODO() - override fun getMousePositionHF() = TODO() - override fun getDefaultFontRenderer() = TODO() - override fun getKeyboardConstants() = TODO() - override fun getScaledWidth() = TODO() - override fun getScaledHeight() = TODO() - override fun getScaleFactor() = TODO() - override fun isOnMacOs() = TODO() - override fun isMouseButtonDown(mouseButton: Int) = TODO() - override fun isKeyboardKeyDown(keyCode: Int) = TODO() - override fun addExtraBuiltinConfigProcessors(processor: io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor<*>) = TODO() - override fun sendClickableChatMessage(message: io.github.notenoughupdates.moulconfig.common.text.StructuredText, action: String, clickType: ClickType?) = TODO() - override fun getKeyName(keyCode: Int) = TODO() - override fun createLiteral(text: String) = TODO() - override fun createTranslatable(key: String, vararg args: io.github.notenoughupdates.moulconfig.common.text.StructuredText) = TODO() - override fun createStructuredTextInternal(`object`: Any) = TODO() - override fun registerPlatformTypeMorphisms(universe: io.github.notenoughupdates.moulconfig.xml.XMLUniverse) = TODO() - @Deprecated("See parent deprecation") - override fun provideTopLevelRenderContext() = TODO() - override fun openWrappedScreen(guiContext: io.github.notenoughupdates.moulconfig.gui.GuiContext) = TODO() - override fun copyToClipboard(string: String) = TODO() - override fun copyFromClipboard() = TODO() -} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponentTest.java b/common/src/test/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponentTest.java new file mode 100644 index 000000000..0b96731c8 --- /dev/null +++ b/common/src/test/java/io/github/notenoughupdates/moulconfig/gui/component/SliderComponentTest.java @@ -0,0 +1,75 @@ +package io.github.notenoughupdates.moulconfig.gui.component; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.IItemStack; +import io.github.notenoughupdates.moulconfig.common.Layer; +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.RenderContext; +import io.github.notenoughupdates.moulconfig.common.TextureFilter; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext; +import io.github.notenoughupdates.moulconfig.observer.GetSetter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.function.Consumer; + +public class SliderComponentTest { + @Test + void renderAcceptsIntegerValue() { + renderSliderWithValue(3); + } + + @Test + void renderAcceptsFloatValue() { + renderSliderWithValue(3F); + } + + @Test + void renderAcceptsDoubleValue() { + renderSliderWithValue(3D); + } + + private void renderSliderWithValue(Number value) { + SliderComponent slider = new SliderComponent(GetSetter.floating(value), 0F, 10F, 1F, 80); + GuiImmediateContext context = new GuiImmediateContext(new NoOpRenderContext(), 0, 0, 80, 16, 0, 0, 0, 0, 0F, 0F); + + Assertions.assertDoesNotThrow(() -> slider.render(context)); + } + + private static class NoOpRenderContext implements RenderContext { + @Override public void pushMatrix() {} + @Override public void popMatrix() {} + @Override public void translate(float x, float y) {} + @Override public void scale(float x, float y) {} + @Override public void drawOnTop(Layer layer, ScissorBehaviour escapeScissors, Consumer later) {} + @Override public void drawColouredQuads(int colour, float... coordinates) {} + @Override public void drawString(IFontRenderer fontRenderer, StructuredText text, int x, int y, int color, boolean shadow) {} + @Override public void drawColoredRect(float left, float top, float right, float bottom, int color) {} + @Override public void invertedRect(float left, float top, float right, float bottom, int additiveColor) {} + @Override public void drawTexturedTintedRect( + MyResourceLocation texture, + float x, + float y, + float width, + float height, + float u1, + float v1, + float u2, + float v2, + int color, + TextureFilter filter + ) {} + @Override public void drawDarkRect(int x, int y, int width, int height, boolean shadow) {} + @Override public void drawGradientRect(int left, int top, int right, int bottom, int startColor, int endColor) {} + @Override public void pushScissor(int left, int top, int right, int bottom) {} + @Override public void pushRawScissor(int left, int top, int right, int bottom) {} + @Override public void popScissor() {} + @Override public void assertNoScissors() {} + @Override public void clearScissor() {} + @Override public void renderItemStack(IItemStack itemStack, int x, int y, StructuredText overlayText) {} + @Override public void drawTooltipNow(int x, int y, List tooltipLines) {} + @Override public void renderExtraLayers() {} + } +} diff --git a/common/src/test/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImplTest.java b/common/src/test/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImplTest.java new file mode 100644 index 000000000..12ecb7ca5 --- /dev/null +++ b/common/src/test/java/io/github/notenoughupdates/moulconfig/processor/ProcessedOptionImplTest.java @@ -0,0 +1,114 @@ +package io.github.notenoughupdates.moulconfig.processor; + +import io.github.notenoughupdates.moulconfig.observer.Property; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; + +public class ProcessedOptionImplTest { + public static class NumericOptions { + public int primitiveInt; + public Integer boxedInteger; + public float primitiveFloat; + public Float boxedFloat; + public double primitiveDouble; + public Double boxedDouble; + public long primitiveLong; + public Long boxedLong; + public short primitiveShort; + public Short boxedShort; + public byte primitiveByte; + public Byte boxedByte; + public Property propertyInteger = Property.of(0); + } + + private final NumericOptions options = new NumericOptions(); + + @Test + void setCoercesPrimitiveInt() { + Assertions.assertTrue(option("primitiveInt").set(14F)); + Assertions.assertEquals(14, options.primitiveInt); + } + + @Test + void setCoercesBoxedInteger() { + Assertions.assertTrue(option("boxedInteger").set(15F)); + Assertions.assertEquals(Integer.valueOf(15), options.boxedInteger); + } + + @Test + void setCoercesPrimitiveFloat() { + Assertions.assertTrue(option("primitiveFloat").set(1D)); + Assertions.assertEquals(1F, options.primitiveFloat); + } + + @Test + void setCoercesBoxedFloat() { + Assertions.assertTrue(option("boxedFloat").set(2D)); + Assertions.assertEquals(Float.valueOf(2F), options.boxedFloat); + } + + @Test + void setCoercesPrimitiveDouble() { + Assertions.assertTrue(option("primitiveDouble").set(3F)); + Assertions.assertEquals(3D, options.primitiveDouble); + } + + @Test + void setCoercesBoxedDouble() { + Assertions.assertTrue(option("boxedDouble").set(4F)); + Assertions.assertEquals(Double.valueOf(4D), options.boxedDouble); + } + + @Test + void setCoercesPrimitiveLong() { + Assertions.assertTrue(option("primitiveLong").set(5F)); + Assertions.assertEquals(5L, options.primitiveLong); + } + + @Test + void setCoercesBoxedLong() { + Assertions.assertTrue(option("boxedLong").set(6F)); + Assertions.assertEquals(Long.valueOf(6L), options.boxedLong); + } + + @Test + void setCoercesPrimitiveShort() { + Assertions.assertTrue(option("primitiveShort").set(7F)); + Assertions.assertEquals(7, options.primitiveShort); + } + + @Test + void setCoercesBoxedShort() { + Assertions.assertTrue(option("boxedShort").set(8F)); + Assertions.assertEquals(Short.valueOf((short) 8), options.boxedShort); + } + + @Test + void setCoercesPrimitiveByte() { + Assertions.assertTrue(option("primitiveByte").set(9F)); + Assertions.assertEquals(9, options.primitiveByte); + } + + @Test + void setCoercesBoxedByte() { + Assertions.assertTrue(option("boxedByte").set(10F)); + Assertions.assertEquals(Byte.valueOf((byte) 10), options.boxedByte); + } + + @Test + void setCoercesPropertyInteger() { + Assertions.assertTrue(option("propertyInteger").set(11F)); + Assertions.assertEquals(Integer.valueOf(11), options.propertyInteger.get()); + } + + private ProcessedOptionImpl option(String fieldName) { + try { + Field field = NumericOptions.class.getField(fieldName); + return new ProcessedOptionImpl(null, null, fieldName, field, null, options, null); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } +} diff --git a/legacy/build.gradle.kts b/legacy/build.gradle.kts index c5059fb50..45c926b50 100644 --- a/legacy/build.gradle.kts +++ b/legacy/build.gradle.kts @@ -1,11 +1,9 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import xyz.wagyourtail.unimined.api.minecraft.task.RemapJarTask plugins { + java id("moulconfig.dokka.base") id("moulconfig.leaf") - id("moulconfig.kotlin") } tasks.withType(JavaCompile::class) { @@ -58,12 +56,6 @@ tasks.processResources { exclude("fabric.mod.json") } -tasks.withType(KotlinCompile::class) { - this.compilerOptions { - this.jvmTarget.set(JvmTarget.JVM_1_8) - } -} - tasks.jar { archiveClassifier.set("small") } @@ -96,5 +88,3 @@ publishing { } } } - - diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.java new file mode 100644 index 000000000..e17eb32bd --- /dev/null +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.java @@ -0,0 +1,52 @@ +package io.github.notenoughupdates.moulconfig.forge; + +import io.github.notenoughupdates.moulconfig.common.IItemStack; +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.internal.ForgeMinecraft; +import net.minecraft.client.Minecraft; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public final class ForgeItemStack implements IItemStack { + private final ItemStack backing; + + private ForgeItemStack(ItemStack backing) { + this.backing = backing; + } + + public ItemStack getBacking() { + return backing; + } + + @Override + public List getLore() { + List result = new ArrayList<>(); + for (String line : backing.getTooltip(Minecraft.getMinecraft().thePlayer, false)) { + result.add(StructuredText.of(line)); + } + return result; + } + + @Override + public StructuredText getDisplayName() { + return StructuredText.of(backing.getDisplayName()); + } + + @Override + public int getStackSize() { + return backing.stackSize; + } + + @Override + public MyResourceLocation getItemId() { + return ForgeMinecraft.fromResourceLocation(Item.itemRegistry.getNameForObject(backing.getItem())); + } + + public static IItemStack of(ItemStack itemStack) { + return new ForgeItemStack(itemStack); + } +} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.kt b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.kt deleted file mode 100644 index 14e84f328..000000000 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/forge/ForgeItemStack.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.notenoughupdates.moulconfig.forge - -import io.github.notenoughupdates.moulconfig.common.IItemStack -import io.github.notenoughupdates.moulconfig.common.MyResourceLocation -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.internal.ForgeMinecraft -import net.minecraft.client.Minecraft -import net.minecraft.item.Item -import net.minecraft.item.ItemStack - -class ForgeItemStack private constructor(val backing: ItemStack) : IItemStack { - override fun getLore(): List { - return backing.getTooltip(Minecraft.getMinecraft().thePlayer, false) - .map { StructuredText.of(it) } - } - - override fun getDisplayName(): StructuredText { - return StructuredText.of(backing.displayName) - } - - override fun getStackSize(): Int { - return backing.stackSize - } - - override fun getItemId(): MyResourceLocation { - return ForgeMinecraft.fromResourceLocation(Item.itemRegistry.getNameForObject(backing.item)) - } - - companion object { - @JvmStatic - fun of(itemStack: ItemStack): IItemStack { - return ForgeItemStack(itemStack) - } - } - -} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorKeybindL.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorKeybindL.java index 2a5b25371..c6a174d7d 100644 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorKeybindL.java +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/gui/editors/GuiOptionEditorKeybindL.java @@ -22,6 +22,7 @@ import io.github.notenoughupdates.moulconfig.GuiTextures; import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.common.MoulConfigPair; import io.github.notenoughupdates.moulconfig.common.RenderContext; import io.github.notenoughupdates.moulconfig.common.TextureFilter; import io.github.notenoughupdates.moulconfig.common.text.StructuredText; @@ -29,7 +30,6 @@ import io.github.notenoughupdates.moulconfig.internal.KeybindHelper; import io.github.notenoughupdates.moulconfig.internal.TextRenderUtils; import io.github.notenoughupdates.moulconfig.processor.ProcessedOption; -import kotlin.Pair; import lombok.val; import net.minecraft.client.Minecraft; import org.lwjgl.input.Keyboard; @@ -41,7 +41,7 @@ public class GuiOptionEditorKeybindL extends GuiOptionEditor { private final int defaultKeyCode; private boolean editingKeycode; - private Pair lastMousePosition = null; + private MoulConfigPair lastMousePosition = null; public GuiOptionEditorKeybindL(ProcessedOption option, int defaultKeyCode) { super(option); @@ -82,7 +82,7 @@ public void render(RenderContext renderContext, int x, int y, int width) { @Override public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) { - lastMousePosition = new Pair<>(mouseX, mouseY); + lastMousePosition = new MoulConfigPair<>(mouseX, mouseY); if (Mouse.getEventButtonState() && Mouse.getEventButton() != -1 && editingKeycode) { editingKeycode = false; option.set(Mouse.getEventButton() - 100); diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.java new file mode 100644 index 000000000..2f61ce452 --- /dev/null +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.java @@ -0,0 +1,53 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiUtilRenderComponents; +import net.minecraft.util.IChatComponent; + +import java.util.ArrayList; +import java.util.List; + +public class ForgeFontRenderer implements IFontRenderer { + public final FontRenderer font; + + public ForgeFontRenderer(FontRenderer font) { + this.font = font; + } + + @Override + public int getStringWidth(StructuredText string) { + return font.getStringWidth(StructuredTextImpl.unwrap(string).getFormattedText()); + } + + @Override + public int getHeight() { + return font.FONT_HEIGHT; + } + + @Override + public int getStringWidth(String string) { + return font.getStringWidth(string); + } + + @Override + public int getCharWidth(char c) { + return font.getCharWidth(c); + } + + @Override + public List splitText(StructuredText text, int width) { + List components = GuiUtilRenderComponents.splitText(StructuredTextImpl.unwrap(text), width, font, false, false); + List result = new ArrayList<>(); + for (IChatComponent component : components) { + result.add(StructuredTextImpl.wrap(component)); + } + return result; + } + + @Override + public String trimStringToWidth(String string, int width, boolean reverse) { + return font.trimStringToWidth(string, width, reverse); + } +} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.kt b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.kt deleted file mode 100644 index ac06a511a..000000000 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeFontRenderer.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.common.IFontRenderer -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.gui.GuiUtilRenderComponents -import java.util.regex.Pattern - - -class ForgeFontRenderer(val font: FontRenderer) : IFontRenderer { - - override fun getStringWidth(string: StructuredText): Int { - return font.getStringWidth(StructuredTextImpl.unwrap(string).formattedText) - } - - override fun getHeight(): Int { - return font.FONT_HEIGHT - } - - override fun getStringWidth(string: String): Int { - return font.getStringWidth(string) - } - - override fun getCharWidth(char: Char): Int { - return font.getCharWidth(char) - } - - override fun splitText(text: StructuredText, width: Int): List { - val iChatComponents = - GuiUtilRenderComponents.splitText(StructuredTextImpl.unwrap(text), width, font, false, false) - return iChatComponents.map { StructuredTextImpl.wrap(it) } - } - - override fun trimStringToWidth(string: String, width: Int, reverse: Boolean): String { - return font.trimStringToWidth(string, width, reverse) - } -} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.java new file mode 100644 index 000000000..fb31e69c7 --- /dev/null +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.java @@ -0,0 +1,35 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.common.IKeyboardConstants; +import org.lwjgl.input.Keyboard; + +public final class ForgeKeyboardConstants implements IKeyboardConstants { + public static final ForgeKeyboardConstants INSTANCE = new ForgeKeyboardConstants(); + + private ForgeKeyboardConstants() { + } + + @Override public int getBackSpace() { return Keyboard.KEY_BACK; } + @Override public int getCtrlLeft() { return Keyboard.KEY_LCONTROL; } + @Override public int getCtrlRight() { return Keyboard.KEY_RCONTROL; } + @Override public int getCmdLeft() { return Keyboard.KEY_LMETA; } + @Override public int getCmdRight() { return Keyboard.KEY_RMETA; } + @Override public int getShiftLeft() { return Keyboard.KEY_LSHIFT; } + @Override public int getShiftRight() { return Keyboard.KEY_RSHIFT; } + @Override public int getEscape() { return Keyboard.KEY_ESCAPE; } + @Override public int getNone() { return Keyboard.KEY_NONE; } + @Override public int getEnter() { return Keyboard.KEY_RETURN; } + @Override public int getDelete() { return Keyboard.KEY_DELETE; } + @Override public int getUp() { return Keyboard.KEY_UP; } + @Override public int getDown() { return Keyboard.KEY_DOWN; } + @Override public int getRight() { return Keyboard.KEY_RIGHT; } + @Override public int getLeft() { return Keyboard.KEY_LEFT; } + @Override public int getHome() { return Keyboard.KEY_HOME; } + @Override public int getEnd() { return Keyboard.KEY_END; } + @Override public int getKeyA() { return Keyboard.KEY_A; } + @Override public int getKeyC() { return Keyboard.KEY_C; } + @Override public int getKeyX() { return Keyboard.KEY_X; } + @Override public int getKeyV() { return Keyboard.KEY_V; } + @Override public int getKeyN() { return Keyboard.KEY_N; } + @Override public int getKeyF() { return Keyboard.KEY_F; } +} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.kt b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.kt deleted file mode 100644 index 28fd90611..000000000 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeKeyboardConstants.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.common.IKeyboardConstants -import org.lwjgl.input.Keyboard - -object ForgeKeyboardConstants : IKeyboardConstants { - override val backSpace: Int - get() = Keyboard.KEY_BACK - override val ctrlLeft: Int - get() = Keyboard.KEY_LCONTROL - override val ctrlRight: Int - get() = Keyboard.KEY_RCONTROL - override val cmdLeft: Int - get() = Keyboard.KEY_LMETA - override val cmdRight: Int - get() = Keyboard.KEY_RMETA - override val shiftLeft: Int - get() = Keyboard.KEY_LSHIFT - override val shiftRight: Int - get() = Keyboard.KEY_RSHIFT - override val escape: Int - get() = Keyboard.KEY_ESCAPE - override val none: Int - get() = Keyboard.KEY_NONE - override val enter: Int - get() = Keyboard.KEY_RETURN - override val delete: Int - get() = Keyboard.KEY_DELETE - override val up: Int - get() = Keyboard.KEY_UP - override val down: Int - get() = Keyboard.KEY_DOWN - override val right: Int - get() = Keyboard.KEY_RIGHT - override val left: Int - get() = Keyboard.KEY_LEFT - override val home: Int - get() = Keyboard.KEY_HOME - override val end: Int - get() = Keyboard.KEY_END - override val keyA: Int - get() = Keyboard.KEY_A - override val keyC: Int - get() = Keyboard.KEY_C - override val keyX: Int - get() = Keyboard.KEY_X - override val keyV: Int - get() = Keyboard.KEY_V - override val keyN: Int - get() = Keyboard.KEY_N - override val keyF: Int - get() = Keyboard.KEY_F -} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.java new file mode 100644 index 000000000..c893b119a --- /dev/null +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.java @@ -0,0 +1,247 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText; +import io.github.notenoughupdates.moulconfig.common.ClickType; +import io.github.notenoughupdates.moulconfig.common.DynamicTextureReference; +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.IKeyboardConstants; +import io.github.notenoughupdates.moulconfig.common.IMinecraft; +import io.github.notenoughupdates.moulconfig.common.MoulConfigPair; +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.RenderContext; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper; +import io.github.notenoughupdates.moulconfig.gui.GuiContext; +import io.github.notenoughupdates.moulconfig.gui.GuiElement; +import io.github.notenoughupdates.moulconfig.gui.GuiScreenElementWrapper; +import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorKeybindL; +import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorSliderL; +import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorTextL; +import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor; +import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.event.ClickEvent; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.ResourceLocation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +public class ForgeMinecraft implements IMinecraft { + @Override + public InputStream loadResourceLocation(MyResourceLocation resourceLocation) { + try { + return Minecraft.getMinecraft().getResourceManager().getResource(fromMyResourceLocation(resourceLocation)).getInputStream(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public MCLogger getLogger(String label) { + Logger logger = LogManager.getLogger(label); + return new MCLogger() { + @Override public void warn(String text) { logger.warn(text); } + @Override public void info(String text) { logger.info(text); } + @Override public void error(String text, Throwable throwable) { logger.error(text, throwable); } + }; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public void addExtraBuiltinConfigProcessors(MoulConfigProcessor processor) { + processor.registerConfigEditor(ConfigEditorKeybind.class, + (processedOption, keybind) -> new GuiOptionEditorKeybindL(processedOption, keybind.defaultKey())); + processor.registerConfigEditor(ConfigEditorText.class, + (processedOption, configEditorText) -> new GuiOptionEditorTextL(processedOption)); + processor.registerConfigEditor(ConfigEditorSlider.class, + (processedOption, configEditorSlider) -> new GuiOptionEditorSliderL( + processedOption, + configEditorSlider.minValue(), + configEditorSlider.maxValue(), + configEditorSlider.minStep() + )); + } + + @Override + public boolean isDevelopmentEnvironment() { + return (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); + } + + @Override + public int getScaledWidth() { + return new ScaledResolution(Minecraft.getMinecraft()).getScaledWidth(); + } + + @Override + public int getScaledHeight() { + return new ScaledResolution(Minecraft.getMinecraft()).getScaledHeight(); + } + + @Override + public int getScaleFactor() { + return new ScaledResolution(Minecraft.getMinecraft()).getScaleFactor(); + } + + @Override + public void sendClickableChatMessage(StructuredText message, String action, ClickType type) { + IChatComponent component = StructuredTextImpl.unwrap(message); + if (type != null) { + ClickEvent.Action clickAction = type == ClickType.OPEN_LINK ? ClickEvent.Action.OPEN_URL : ClickEvent.Action.RUN_COMMAND; + component.setChatStyle(component.getChatStyle().setChatClickEvent(new ClickEvent(clickAction, action))); + } + Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessage(component); + } + + @Override + public DynamicTextureReference generateDynamicTexture(BufferedImage image) { + DynamicTexture texture = new DynamicTexture(image); + ResourceLocation res = Minecraft.getMinecraft().getTextureManager().getDynamicTextureLocation("moulconfigdyn", texture); + return new DynamicTextureReference() { + @Override + public MyResourceLocation getIdentifier() { + return fromResourceLocation(res); + } + + @Override + public void update(BufferedImage bufferedImage) { + bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), texture.getTextureData(), 0, bufferedImage.getWidth()); + texture.updateDynamicTexture(); + } + + @Override + protected void doDestroy() { + Minecraft.getMinecraft().getTextureManager().deleteTexture(res); + } + }; + } + + @Override + public boolean isGeneratedSentinel(MyResourceLocation resourceLocation) { + return "moulconfigdyn".equals(resourceLocation.getRoot()); + } + + @Override + public StructuredText getKeyName(int keyCode) { + return StructuredText.of(KeybindHelper.getKeyName(keyCode)); + } + + @Override + public StructuredText.Mutable createLiteral(String text) { + return StructuredTextImpl.wrap(new ChatComponentText(text)); + } + + @Override + public StructuredText.Mutable createTranslatable(String key, StructuredText... args) { + return StructuredTextImpl.wrap(new ChatComponentTranslation(key, (Object[]) args)); + } + + @Override + public StructuredText createStructuredTextInternal(Object obj) { + if (obj instanceof IChatComponent) { + return StructuredTextImpl.wrap((IChatComponent) obj); + } + return null; + } + + @Override + public void registerPlatformTypeMorphisms(XMLUniverse universe) { + } + + @Override + public boolean isMouseButtonDown(int mouseButton) { + return Mouse.isButtonDown(mouseButton); + } + + @Override + public boolean isKeyboardKeyDown(int keyboardKey) { + return Keyboard.isKeyDown(keyboardKey); + } + + @Override + public RenderContext provideTopLevelRenderContext() { + return new ForgeRenderContext(); + } + + public void openScreen(GuiScreen gui) { + Minecraft.getMinecraft().displayGuiScreen(gui); + } + + @Override + public void openWrappedScreen(GuiElement gui) { + openScreen(new GuiScreenElementWrapper(gui)); + } + + @Override + public void openWrappedScreen(GuiContext gui) { + openScreen(new GuiComponentWrapper(gui)); + } + + @Override + public void copyToClipboard(String string) { + try { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(string), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public String copyFromClipboard() { + try { + Object value = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor); + return value instanceof String ? (String) value : ""; + } catch (Exception e) { + return ""; + } + } + + @Override + public MoulConfigPair getMousePositionHF() { + ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft()); + double width = sr.getScaledWidth_double(); + double mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; + double height = sr.getScaledHeight_double(); + double mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; + return new MoulConfigPair<>(mouseX, mouseY); + } + + public static ResourceLocation fromMyResourceLocation(MyResourceLocation resourceLocation) { + return new ResourceLocation(resourceLocation.getRoot(), resourceLocation.getPath()); + } + + public static MyResourceLocation fromResourceLocation(ResourceLocation resourceLocation) { + return new MyResourceLocation(resourceLocation.getResourceDomain(), resourceLocation.getResourcePath()); + } + + @Override + public boolean isOnMacOs() { + return Minecraft.isRunningOnMac; + } + + @Override + public IFontRenderer getDefaultFontRenderer() { + return new ForgeFontRenderer(Minecraft.getMinecraft().fontRendererObj); + } + + @Override + public IKeyboardConstants getKeyboardConstants() { + return ForgeKeyboardConstants.INSTANCE; + } +} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.kt b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.kt deleted file mode 100644 index 47818636c..000000000 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeMinecraft.kt +++ /dev/null @@ -1,247 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText -import io.github.notenoughupdates.moulconfig.common.* -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper -import io.github.notenoughupdates.moulconfig.gui.GuiContext -import io.github.notenoughupdates.moulconfig.gui.GuiElement -import io.github.notenoughupdates.moulconfig.gui.GuiScreenElementWrapper -import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorKeybindL -import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorSliderL -import io.github.notenoughupdates.moulconfig.gui.editors.GuiOptionEditorTextL -import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor -import io.github.notenoughupdates.moulconfig.xml.XMLUniverse -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiScreen -import net.minecraft.client.gui.ScaledResolution -import net.minecraft.client.renderer.texture.DynamicTexture -import net.minecraft.event.ClickEvent -import net.minecraft.launchwrapper.Launch -import net.minecraft.util.ChatComponentText -import net.minecraft.util.ChatComponentTranslation -import net.minecraft.util.IChatComponent -import net.minecraft.util.ResourceLocation -import org.apache.logging.log4j.LogManager -import org.lwjgl.input.Keyboard -import org.lwjgl.input.Mouse -import java.awt.Toolkit -import java.awt.datatransfer.DataFlavor -import java.awt.datatransfer.StringSelection -import java.awt.image.BufferedImage -import java.io.InputStream - -class ForgeMinecraft : IMinecraft { - override fun loadResourceLocation(resourceLocation: MyResourceLocation): InputStream { - return Minecraft.getMinecraft().resourceManager.getResource(fromMyResourceLocation(resourceLocation)) - .inputStream - } - - override fun getLogger(label: String): MCLogger { - val logger = LogManager.getLogger(label) - return object : MCLogger { - override fun warn(text: String) { - logger.warn(text) - } - - override fun info(text: String) { - logger.info(text) - } - - override fun error(text: String, throwable: Throwable) { - logger.error(text, throwable) - } - - } - } - - override fun addExtraBuiltinConfigProcessors(processor: MoulConfigProcessor<*>) { - processor.registerConfigEditor( - ConfigEditorKeybind::class.java - ) { processedOption, keybind: ConfigEditorKeybind -> - GuiOptionEditorKeybindL( - processedOption, - keybind.defaultKey - ) - } - processor.registerConfigEditor( - ConfigEditorText::class.java - ) { processedOption, configEditorText: ConfigEditorText? -> - GuiOptionEditorTextL( - processedOption - ) - } - processor.registerConfigEditor( - ConfigEditorSlider::class.java - ) { processedOption, configEditorSlider: ConfigEditorSlider -> - GuiOptionEditorSliderL( - processedOption, - configEditorSlider.minValue, - configEditorSlider.maxValue, - configEditorSlider.minStep - ) - } - } - - override fun isDevelopmentEnvironment(): Boolean { - return Launch.blackboard.get("fml.deobfuscatedEnvironment") as Boolean - } - - override fun getScaledWidth(): Int { - return ScaledResolution(Minecraft.getMinecraft()).scaledWidth - } - - override fun getScaledHeight(): Int { - return ScaledResolution(Minecraft.getMinecraft()).scaledHeight - } - - override fun getScaleFactor(): Int { - return ScaledResolution(Minecraft.getMinecraft()).scaleFactor - } - - override fun sendClickableChatMessage(message: StructuredText, action: String, type: ClickType?) { - val component = StructuredTextImpl.unwrap(message) - if (type != null) - component.chatStyle = component.chatStyle - .setChatClickEvent( - ClickEvent( - when (type) { - ClickType.OPEN_LINK -> ClickEvent.Action.OPEN_URL - ClickType.RUN_COMMAND -> ClickEvent.Action.RUN_COMMAND - }, action - ) - ) - Minecraft.getMinecraft().ingameGUI.chatGUI.printChatMessage( - component - - ) - } - - override fun generateDynamicTexture(image: BufferedImage): DynamicTextureReference { - val texture = DynamicTexture(image) - val res = Minecraft.getMinecraft().textureManager.getDynamicTextureLocation("moulconfigdyn", texture) - - return object : DynamicTextureReference() { - override val identifier: MyResourceLocation - get() = fromResourceLocation(res) - - override fun update(bufferedImage: BufferedImage) { - bufferedImage.getRGB( - 0, 0, bufferedImage.width, bufferedImage.height, - texture.textureData, 0, bufferedImage.width - ) - texture.updateDynamicTexture() - } - - override fun doDestroy() { - Minecraft.getMinecraft().textureManager.deleteTexture(res) - } - } - } - - override fun isGeneratedSentinel(resourceLocation: MyResourceLocation): Boolean { - return resourceLocation.root == "moulconfigdyn" // technically this will also start with dynamic/ but i dont control that, so i will just use another namespace smilers - } - - override fun getKeyName(keyCode: Int): StructuredText { - return StructuredText.of(KeybindHelper.getKeyName(keyCode)) - } - - override fun createLiteral(text: String): StructuredText.Mutable { - return StructuredTextImpl.wrap(ChatComponentText(text)) - } - - override fun createTranslatable(key: String, vararg args: StructuredText): StructuredText.Mutable { - return StructuredTextImpl.wrap(ChatComponentTranslation(key, *args)) - } - - override fun createStructuredTextInternal(obj: Any): StructuredText? { - if (obj is IChatComponent) - return StructuredTextImpl.wrap(obj) - return null - } - - override fun registerPlatformTypeMorphisms(universe: XMLUniverse) { - // TODO: add more platform morphisms.. oh well this platform is basically EOL anyways... - } - - override fun isMouseButtonDown(mouseButton: Int): Boolean { - return Mouse.isButtonDown(mouseButton) - } - - override fun isKeyboardKeyDown(keyboardKey: Int): Boolean { - return Keyboard.isKeyDown(keyboardKey) - } - - override fun provideTopLevelRenderContext(): RenderContext { - return ForgeRenderContext() - } - - fun openScreen(gui: GuiScreen) { - Minecraft.getMinecraft().displayGuiScreen(gui) - } - - override fun openWrappedScreen(gui: GuiElement) { - openScreen(GuiScreenElementWrapper(gui)) - } - - override fun openWrappedScreen(gui: GuiContext) { - openScreen(GuiComponentWrapper(gui)) - } - - override fun copyToClipboard(string: String) { - try { - Toolkit.getDefaultToolkit() - .systemClipboard - .setContents(StringSelection(string), null) - } catch (e: Exception) { - e.printStackTrace() - } - } - - override fun copyFromClipboard(): String { - return try { - Toolkit.getDefaultToolkit().systemClipboard.getContents(null).getTransferData(DataFlavor.stringFlavor) as String - } catch (e: Exception) { - null - } ?: "" - } - - override fun getMousePositionHF(): Pair { - val sr = ScaledResolution(Minecraft.getMinecraft()) - val width = sr.scaledWidth_double - val mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth - val height = sr.scaledHeight_double - val mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1 - return mouseX to mouseY - } - - companion object { - @JvmStatic - fun fromMyResourceLocation(resourceLocation: MyResourceLocation): ResourceLocation { - return ResourceLocation( - resourceLocation.root, - resourceLocation.path - ) - } - - @JvmStatic - fun fromResourceLocation(resouceLocation: ResourceLocation): MyResourceLocation { - return MyResourceLocation(resouceLocation.resourceDomain, resouceLocation.resourcePath) - } - } - - override fun isOnMacOs(): Boolean { - return Minecraft.isRunningOnMac - } - - override fun getDefaultFontRenderer(): IFontRenderer { - return ForgeFontRenderer(Minecraft.getMinecraft().fontRendererObj) - } - - override fun getKeyboardConstants(): IKeyboardConstants { - return ForgeKeyboardConstants - } -} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.java new file mode 100644 index 000000000..d1b1c7dfc --- /dev/null +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.java @@ -0,0 +1,206 @@ +package io.github.notenoughupdates.moulconfig.internal; + +import io.github.notenoughupdates.moulconfig.common.IFontRenderer; +import io.github.notenoughupdates.moulconfig.common.IItemStack; +import io.github.notenoughupdates.moulconfig.common.Layer; +import io.github.notenoughupdates.moulconfig.common.MyResourceLocation; +import io.github.notenoughupdates.moulconfig.common.RenderContext; +import io.github.notenoughupdates.moulconfig.common.TextureFilter; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; +import io.github.notenoughupdates.moulconfig.forge.ForgeItemStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.IChatComponent; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class ForgeRenderContext implements RenderContext { + @Override + public void pushMatrix() { + GlStateManager.pushMatrix(); + } + + @Override + public void popMatrix() { + GlStateManager.popMatrix(); + } + + @Override + public void translate(float x, float y) { + GlStateManager.translate(x, y, 0F); + } + + @Override + public void scale(float x, float y) { + GlStateManager.scale(x, y, 1F); + } + + @Override + public boolean isMouseButtonDown(int mouseButton) { + return Mouse.isButtonDown(mouseButton); + } + + @Override + public boolean isKeyboardKeyDown(int keyboardKey) { + return Keyboard.isKeyDown(keyboardKey); + } + + @Override + public void drawString(IFontRenderer fontRenderer, StructuredText text, int x, int y, int color, boolean shadow) { + ((ForgeFontRenderer) fontRenderer).font.drawString(StructuredTextImpl.unwrap(text).getFormattedText(), (float) x, (float) y, color, shadow); + } + + @Override + public void drawColoredRect(float left, float top, float right, float bottom, int color) { + RenderUtils.drawGradientRect(0, (int) left, (int) top, (int) right, (int) bottom, color, color); + } + + public void applyGlobalColor(int color) { + GlStateManager.color( + ColourUtil.unpackARGBRedF(color), + ColourUtil.unpackARGBGreenF(color), + ColourUtil.unpackARGBBlueF(color), + ColourUtil.unpackARGBAlphaF(color) + ); + } + + @Override + public void drawColouredQuads(int colour, float... coordinates) { + Tessellator tessellator = Tessellator.getInstance(); + net.minecraft.client.renderer.WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + GlStateManager.enableBlend(); + GlStateManager.disableTexture2D(); + GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0); + worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); + applyGlobalColor(colour); + if (coordinates.length % 8 != 0) { + throw new IllegalArgumentException("Quad coordinates must be groups of four points"); + } + for (int i = 0; i < coordinates.length / 2; i++) { + worldrenderer.pos(coordinates[i * 2], coordinates[i * 2 + 1], 0.0).endVertex(); + } + tessellator.draw(); + GlStateManager.enableTexture2D(); + GlStateManager.disableBlend(); + } + + @Override + public void drawGradientRect(int left, int top, int right, int bottom, int startColor, int endColor) { + RenderUtils.drawGradientRect(0, left, top, right, bottom, startColor, endColor); + } + + @Override + public void invertedRect(float left, float top, float right, float bottom, int additiveColor) { + GlStateManager.enableColorLogic(); + GlStateManager.colorLogicOp(GL11.GL_OR_REVERSE); + GlStateManager.disableTexture2D(); + Tessellator tessellator = Tessellator.getInstance(); + net.minecraft.client.renderer.WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + applyGlobalColor(additiveColor); + worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION); + worldrenderer.pos(right, top, 0.0).endVertex(); + worldrenderer.pos(left, top, 0.0).endVertex(); + worldrenderer.pos(left, bottom, 0.0).endVertex(); + worldrenderer.pos(right, bottom, 0.0).endVertex(); + tessellator.draw(); + GlStateManager.enableTexture2D(); + GlStateManager.disableColorLogic(); + } + + @Override + public void drawTexturedTintedRect(MyResourceLocation texture, float x, float y, float width, float height, float u1, float v1, float u2, float v2, int color, TextureFilter filter) { + FilterAssertionCache.assertTextureFilter(texture, filter); + applyGlobalColor(color); + Minecraft.getMinecraft().getTextureManager().bindTexture(ForgeMinecraft.fromMyResourceLocation(texture)); + RenderUtils.drawTexturedRect( + x, y, width, height, u1, u2, v1, v2, + filter == TextureFilter.LINEAR ? GL11.GL_LINEAR : GL11.GL_NEAREST + ); + } + + @Override + public void drawDarkRect(int x, int y, int width, int height, boolean shadow) { + RenderUtils.drawFloatingRectDark(x, y, width, height, shadow); + } + + @Override + public void pushScissor(int left, int top, int right, int bottom) { + GlScissorStack.push(left, top, right, bottom, new ScaledResolution(Minecraft.getMinecraft()), false); + } + + @Override + public void pushRawScissor(int left, int top, int right, int bottom) { + GlScissorStack.push(left, top, right, bottom, new ScaledResolution(Minecraft.getMinecraft()), true); + } + + @Override + public void popScissor() { + GlScissorStack.pop(new ScaledResolution(Minecraft.getMinecraft())); + } + + @Override + public void assertNoScissors() { + if (!GlScissorStack.isEmpty()) { + Warnings.warn("no scissor assertion failed", 4); + } + } + + @Override + public void clearScissor() { + GlScissorStack.clear(); + } + + @Override + public void renderItemStack(IItemStack itemStack, int x, int y, StructuredText overlayText) { + ForgeItemStack forgeStack = (ForgeItemStack) itemStack; + net.minecraft.item.ItemStack backing = forgeStack.getBacking(); + net.minecraft.client.renderer.entity.RenderItem renderItem = Minecraft.getMinecraft().getRenderItem(); + RenderHelper.enableGUIStandardItemLighting(); + renderItem.renderItemAndEffectIntoGUI(backing, x, y); + if (overlayText != null) { + renderItem.renderItemOverlayIntoGUI(Minecraft.getMinecraft().fontRendererObj, backing, x, y, overlayText.getText()); + } + RenderHelper.disableStandardItemLighting(); + } + + @Override + public void drawTooltipNow(int x, int y, List tooltipLines) { + ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft()); + int width = scaledResolution.getScaledWidth(); + int height = scaledResolution.getScaledHeight(); + int mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; + int mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; + List components = new ArrayList<>(); + for (StructuredText line : tooltipLines) { + components.add(StructuredTextImpl.unwrap(line)); + } + TextRenderUtils.drawHoveringText(components, mouseX, mouseY, width, height, -1, Minecraft.getMinecraft().fontRendererObj); + } + + @Override + public void drawOnTop(Layer layer, ScissorBehaviour scissorBehaviour, Consumer later) { + pushMatrix(); + if (scissorBehaviour == ScissorBehaviour.ESCAPE) { + pushRawScissor(0, 0, getMinecraft().getScaledWidth(), getMinecraft().getScaledHeight()); + } + GlStateManager.translate(0F, 0F, (float) layer.getSortIndex()); + later.accept(this); + if (scissorBehaviour == ScissorBehaviour.ESCAPE) { + popScissor(); + } + popMatrix(); + } + + @Override + public void renderExtraLayers() { + } +} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.kt b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.kt deleted file mode 100644 index 3a929ad36..000000000 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/ForgeRenderContext.kt +++ /dev/null @@ -1,224 +0,0 @@ -package io.github.notenoughupdates.moulconfig.internal - -import io.github.notenoughupdates.moulconfig.common.* -import io.github.notenoughupdates.moulconfig.common.text.StructuredText -import io.github.notenoughupdates.moulconfig.forge.ForgeItemStack -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.ScaledResolution -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.RenderHelper -import net.minecraft.client.renderer.Tessellator -import net.minecraft.client.renderer.vertex.DefaultVertexFormats -import org.lwjgl.input.Keyboard -import org.lwjgl.input.Mouse -import org.lwjgl.opengl.GL11 -import java.util.function.Consumer - -class ForgeRenderContext : RenderContext { - override fun pushMatrix() { - GlStateManager.pushMatrix() - } - - override fun popMatrix() { - GlStateManager.popMatrix() - } - - override fun translate(x: Float, y: Float) { - GlStateManager.translate(x, y, 0F) - } - - override fun scale(x: Float, y: Float) { - GlStateManager.scale(x, y, 1f) - } - - override fun isMouseButtonDown(mouseButton: Int): Boolean { - return Mouse.isButtonDown(mouseButton) - } - - override fun isKeyboardKeyDown(keyboardKey: Int): Boolean { - return Keyboard.isKeyDown(keyboardKey) - } - - override fun drawString(fontRenderer: IFontRenderer, text: StructuredText, x: Int, y: Int, color: Int, shadow: Boolean) { - (fontRenderer as ForgeFontRenderer).font.drawString(StructuredTextImpl.unwrap(text).formattedText, x.toFloat(), y.toFloat(), color, shadow) - } - - - override fun drawColoredRect(left: Float, top: Float, right: Float, bottom: Float, color: Int) { - RenderUtils.drawGradientRect( - 0, - left.toInt(), - top.toInt(), - right.toInt(), - bottom.toInt(), - color, - color - ) - } - - fun applyGlobalColor(color: Int) { - GlStateManager.color(ColourUtil.unpackARGBRedF(color), ColourUtil.unpackARGBGreenF(color), ColourUtil.unpackARGBBlueF(color), ColourUtil.unpackARGBAlphaF(color)) - } - - - override fun drawColouredQuads(colour: Int, vararg coordinates: Float) { - val tessellator = Tessellator.getInstance() - val worldrenderer = tessellator.worldRenderer - GlStateManager.enableBlend() - GlStateManager.disableTexture2D() - GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0) - worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION) - - applyGlobalColor(colour) - require(coordinates.size % 8 == 0) - for (i in 0 until (coordinates.size / 2)) { - worldrenderer.pos(coordinates[i * 2].toDouble(), coordinates[i * 2 + 1].toDouble(), 0.0).endVertex() - } - - tessellator.draw() - GlStateManager.enableTexture2D() - GlStateManager.disableBlend() - } - - override fun drawGradientRect( - left: Int, - top: Int, - right: Int, - bottom: Int, - startColor: Int, - endColor: Int - ) { - RenderUtils.drawGradientRect( - 0, - left, - top, - right, - bottom, - startColor, - endColor - ) - } - - override fun invertedRect(left: Float, top: Float, right: Float, bottom: Float, additiveColor: Int) { - GlStateManager.enableColorLogic() - GlStateManager.colorLogicOp(GL11.GL_OR_REVERSE) - GlStateManager.disableTexture2D() - val tessellator = Tessellator.getInstance() - val worldrenderer = tessellator.worldRenderer - applyGlobalColor(additiveColor) - worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION) - worldrenderer.pos(right.toDouble(), top.toDouble(), 0.0).endVertex() - worldrenderer.pos(left.toDouble(), top.toDouble(), 0.0).endVertex() - worldrenderer.pos(left.toDouble(), bottom.toDouble(), 0.0).endVertex() - worldrenderer.pos(right.toDouble(), bottom.toDouble(), 0.0).endVertex() - tessellator.draw() - GlStateManager.enableTexture2D() - GlStateManager.disableColorLogic() - } - - override fun drawTexturedTintedRect(texture: MyResourceLocation, x: Float, y: Float, width: Float, height: Float, u1: Float, v1: Float, u2: Float, v2: Float, color: Int, filter: TextureFilter) { - FilterAssertionCache.assertTextureFilter(texture, filter) - applyGlobalColor(color) - Minecraft.getMinecraft().textureManager.bindTexture(ForgeMinecraft.fromMyResourceLocation(texture)) - RenderUtils.drawTexturedRect( - x, y, - width, height, - u1, u2, v1, v2, - when (filter) { - TextureFilter.LINEAR -> GL11.GL_LINEAR - TextureFilter.NEAREST -> GL11.GL_NEAREST - } - ) - } - - override fun drawDarkRect(x: Int, y: Int, width: Int, height: Int, shadow: Boolean) { - RenderUtils.drawFloatingRectDark( - x, - y, - width, - height, - shadow - ) - } - - override fun pushScissor(left: Int, top: Int, right: Int, bottom: Int) { // TODO: make this translate (by reading out the matrix state, sadly) - GlScissorStack.push( - left, - top, - right, - bottom, - ScaledResolution(Minecraft.getMinecraft()), - false - ) - } - - override fun pushRawScissor(left: Int, top: Int, right: Int, bottom: Int) { - GlScissorStack.push( - left, - top, - right, - bottom, - ScaledResolution(Minecraft.getMinecraft()), - true - ) - } - - override fun popScissor() { - GlScissorStack.pop(ScaledResolution(Minecraft.getMinecraft())) - } - - override fun assertNoScissors() { - if (!GlScissorStack.isEmpty()) - Warnings.warn("no scissor assertion failed", 4) - } - - override fun clearScissor() { - GlScissorStack.clear() - } - - override fun renderItemStack(itemStack: IItemStack, x: Int, y: Int, overlayText: StructuredText?) { - val forgeStack = itemStack as ForgeItemStack - val backing = forgeStack.backing - val renderItem = Minecraft.getMinecraft().renderItem - RenderHelper.enableGUIStandardItemLighting() - renderItem.renderItemAndEffectIntoGUI(backing, x, y) - if (overlayText != null) renderItem.renderItemOverlayIntoGUI( - Minecraft.getMinecraft().fontRendererObj, - backing, - x, - y, - overlayText.text - ) - RenderHelper.disableStandardItemLighting() - } - - - override fun drawTooltipNow(x: Int, y: Int, tooltipLines: List) { - val scaledResolution = ScaledResolution(Minecraft.getMinecraft()) - val width = scaledResolution.scaledWidth - val height = scaledResolution.scaledHeight - val mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth - val mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1 - TextRenderUtils.drawHoveringText( - tooltipLines.map { StructuredTextImpl.unwrap(it) }, mouseX, mouseY, - width, height, -1, Minecraft.getMinecraft().fontRendererObj - ) - } - - override fun drawOnTop(layer: Layer, scissorBehaviour: RenderContext.ScissorBehaviour, later: Consumer) { - pushMatrix() - if (scissorBehaviour == RenderContext.ScissorBehaviour.ESCAPE) { - pushRawScissor(0, 0, minecraft.scaledWidth, minecraft.scaledHeight) - } - GlStateManager.translate(0F, 0F, layer.sortIndex.toFloat()) - later.accept(this) - if (scissorBehaviour == RenderContext.ScissorBehaviour.ESCAPE) { - popScissor() - } - popMatrix() - } - - override fun renderExtraLayers() { - // Left blank: [drawOnTop] renders directly. - } -} diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/RPModContainer.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/RPModContainer.java index 5ec17d721..0fa6a62a1 100644 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/RPModContainer.java +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/internal/RPModContainer.java @@ -2,7 +2,6 @@ import com.google.common.eventbus.EventBus; import io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker; -import lombok.var; import net.minecraftforge.fml.client.FMLFileResourcePack; import net.minecraftforge.fml.common.DummyModContainer; import net.minecraftforge.fml.common.LoadController; @@ -53,7 +52,7 @@ public RPModContainer() { } public List explore(ASMDataTable table) { - var md = new ModMetadata(); + ModMetadata md = new ModMetadata(); md.modId = "moulconfigrp"; md.name = "MoulConfig Resource Pack"; md.autogenerated = true; diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/test/MoulConfigTest.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/test/MoulConfigTest.java index b5f29f2a1..4678ca882 100644 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/test/MoulConfigTest.java +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/test/MoulConfigTest.java @@ -37,7 +37,6 @@ import io.github.notenoughupdates.moulconfig.xml.Bind; import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; import lombok.SneakyThrows; -import lombok.var; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; @@ -120,8 +119,8 @@ public void processCommand(ICommandSender sender, String[] args) { )) )); } else if (args.length > 0 && "testxml".equals(args[0])) { - var xmlUniverse = XMLUniverse.getDefaultUniverse(); - var gui = xmlUniverse.load(new ObjectBound(), Minecraft.getMinecraft().getResourceManager() + XMLUniverse xmlUniverse = XMLUniverse.getDefaultUniverse(); + GuiComponent gui = xmlUniverse.load(new ObjectBound(), Minecraft.getMinecraft().getResourceManager() .getResource(new ResourceLocation("moulconfig:test.xml")).getInputStream()); screenToOpen = new GuiComponentWrapper(new GuiContext(gui)); } else { diff --git a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/tweaker/ModDiscovererTransformer.java b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/tweaker/ModDiscovererTransformer.java index c500a059a..8b5829688 100644 --- a/legacy/src/main/java/io/github/notenoughupdates/moulconfig/tweaker/ModDiscovererTransformer.java +++ b/legacy/src/main/java/io/github/notenoughupdates/moulconfig/tweaker/ModDiscovererTransformer.java @@ -1,7 +1,6 @@ package io.github.notenoughupdates.moulconfig.tweaker; import io.github.notenoughupdates.moulconfig.internal.RPModContainer; -import lombok.var; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; @@ -14,7 +13,7 @@ public ModDiscovererTransformer(ClassWriter writer) { @Override public MethodVisitor visitMethod(int mAccess, String name, String desc, String signature, String[] exceptions) { - var sup = super.visitMethod(mAccess, name, desc, signature, exceptions); + MethodVisitor sup = super.visitMethod(mAccess, name, desc, signature, exceptions); if (name.equals("identifyMods")) { return new MethodVisitor(Opcodes.ASM5, sup) { @Override diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/platform/MoulConfigPlatform.java b/modern/templates/java/io/github/notenoughupdates/moulconfig/platform/MoulConfigPlatform.java index 1b59e0bc8..8ba51bc35 100644 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/platform/MoulConfigPlatform.java +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/platform/MoulConfigPlatform.java @@ -9,7 +9,6 @@ import io.github.notenoughupdates.moulconfig.internal.Warnings; import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor; import io.github.notenoughupdates.moulconfig.xml.XMLUniverse; -import kotlin.Pair; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.fabricmc.loader.api.FabricLoader; @@ -179,7 +178,7 @@ protected void doDestroy() { } @Override - public Pair getMousePositionHF() { + public MoulConfigPair getMousePositionHF() { var mouse = mc.mouseHandler; var window = mc.getWindow(); #if MC < 12111 @@ -189,7 +188,7 @@ public Pair getMousePositionHF() { double x = mouse.getScaledXPos(window); double y = mouse.getScaledYPos(window); #endif - return new Pair<>(x, y); + return new MoulConfigPair<>(x, y); } @Override diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/FabricMain.java b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/FabricMain.java index 9c5658bb2..6ee404d85 100644 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/FabricMain.java +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/FabricMain.java @@ -41,7 +41,7 @@ public void onInitialize() { XMLUniverse xmlUniverse = XMLUniverse.getDefaultUniverse(); var scene = xmlUniverse.load( new ObjectBound(), - IMinecraft.INSTANCE.loadResourceLocation(MyResourceLocation.Companion.parse("moulconfig:test.xml")) + IMinecraft.INSTANCE.loadResourceLocation(MyResourceLocation.parse("moulconfig:test.xml")) ); IMinecraft.getInstance() .openWrappedScreen(scene); diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.kt b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.java similarity index 59% rename from modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.kt rename to modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.java index d029b5c4e..7bb2185d4 100644 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.kt +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/InnerCategory.java @@ -1,12 +1,10 @@ -package io.github.notenoughupdates.moulconfig.test +package io.github.notenoughupdates.moulconfig.test; -import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean -import io.github.notenoughupdates.moulconfig.annotations.ConfigOption - -class InnerCategory { +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; +public class InnerCategory { @ConfigEditorBoolean @ConfigOption(name = "Test Option", desc = "Test toggle") - var shouldTestToggle: Boolean = false - + public boolean shouldTestToggle = false; } diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.java b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.java new file mode 100644 index 000000000..22665a66f --- /dev/null +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.java @@ -0,0 +1,137 @@ +package io.github.notenoughupdates.moulconfig.test; + +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.ChromaColour; +import io.github.notenoughupdates.moulconfig.annotations.Accordion; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOrder; +import io.github.notenoughupdates.moulconfig.observer.Property; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TestCategoryA { + @Expose + @ConfigOrder(Integer.MAX_VALUE) + @ConfigOption(name = "Bottom option!", desc = "Declared at top, but should appear at the bottom by Order annotation.") + @ConfigEditorInfoText(infoTitle = "Bottom option") + public boolean bottomOption = false; + + @ConfigOption(name = "Open Wide", desc = "Use a wider config menu") + @ConfigEditorBoolean + public boolean isWide = false; + + public boolean isWide() { + return isWide; + } + + @ConfigOption(name = "Test Option", desc = "Test toggle") + @ConfigEditorBoolean + public boolean shouldTestToggle = false; + + @ConfigOption(name = "Pronouns in MoulConfig", desc = "Fuck It") + @ConfigEditorDropdown + public Pronouns dropdownTest = Pronouns.ITITS; + + public enum Pronouns { + HEHIM("He/Him"), SHEHER("She/Her"), ITITS("It/Its"), THEYTHEM("They/Them"), USE_NAME("Use Name"); + private final String label; + Pronouns(String label) { this.label = label; } + @Override public String toString() { return label; } + } + + @Accordion + @ConfigOption(name = "Accordion", desc = "") + public AccordionClass accordion = new AccordionClass(); + + public static class AccordionClass { + @ConfigOption(name = "Number Dropdown", desc = "0, 1, 2, 3") + @ConfigEditorDropdown(values = {"0", "1", "2", "3"}) + public int numberDropdown = 0; + + @Expose + @ConfigOption(name = "Drop Down", desc = "Using strings") + @ConfigEditorDropdown(values = {"A", "B", "C"}) + public String dropDownUisngStrings = "A"; + + @ConfigOption(name = "Enum Dropdown", desc = "1, 2, 3, 4") + @ConfigEditorDropdown + public Property enumDropdown = Property.of(DropdownEnum.FOUR); + } + + public enum DropdownEnum { + ONE("1"), TWO("2"), THREE("3"), FOUR("4"); + private final String label; + DropdownEnum(String label) { this.label = label; } + @Override public String toString() { return label; } + } + + @ConfigOption(name = "Slider", desc = "Between 1 and 5") + @ConfigEditorSlider(minValue = 1F, maxValue = 5F, minStep = 1F) + public int minimumTitle = 1; + + @ConfigOption(name = "Info Box", desc = "Shows important info to the user") + @ConfigEditorInfoText(infoTitle = "\u00a7cAlert") + public String notice = ""; + + @ConfigOption(name = "Text Box", desc = "Lets you put strings.") + @ConfigEditorText(forbidden = "\u00a7z") + public Property customText = Property.of("abc"); + + @ConfigOption(name = "Draggable List", desc = "\u00a7eDrag text to change the order of the list.") + @ConfigEditorDraggableList(exampleText = {"abc", "dec", "blah", "surel it works really cool and great :))"}) + public List draggableList = new ArrayList<>(Arrays.asList(0, 1, 2, 3)); + + @Expose + @ConfigOption(name = "Enum Draggable List", desc = "Draggable list but doesnt work properly.") + @ConfigEditorDraggableList + public List enumDraggableList = new ArrayList<>(Arrays.asList(EnumDraggableList.ONE, EnumDraggableList.THREE, EnumDraggableList.TWO)); + + public enum EnumDraggableList { + ONE("1"), TWO("too"), THREE("three"); + private final String str; + EnumDraggableList(String str) { this.str = str; } + @Override public String toString() { return str; } + } + + @ConfigOption(name = "Colour Test", desc = "Test a colour editor") + @ConfigEditorColour + public ChromaColour colour = new ChromaColour(0F, 1F, 1F, 0, 0xFF); + + @Expose + @ConfigOption(name = "Keybind", desc = "The Number One") + @ConfigEditorKeybind(defaultKey = GLFW.GLFW_KEY_1) + public int slot1 = GLFW.GLFW_KEY_1; + + @Expose + @ConfigOption(name = "Test Runnable", desc = "Test a java.lang.Runnable") + @ConfigEditorButton(buttonText = "Click me") + public final Runnable runnable = () -> System.out.println("JRunnable working"); + + @Expose + @ConfigOption(name = "Test Runnable", desc = "Test a second java.lang.Runnable") + @ConfigEditorButton(buttonText = "Click me") + public final Runnable secondRunnable = () -> System.out.println("Second Runnable working"); + + @Expose + @ConfigOption(name = "Test Runnable", desc = "Test a (ignored) runnable using runnableId to emit a test warning") + @ConfigEditorButton(runnableId = 10, buttonText = "Click me") + public final Object runnableId = new Object(); + + @Expose + @ConfigOrder(-1) + @ConfigOption(name = "Top option!", desc = "Declared at the bottom, floated to top by Order annotation.") + @ConfigEditorInfoText(infoTitle = "Top option") + public boolean topOption = false; +} diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.kt b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.kt deleted file mode 100644 index 33e194be0..000000000 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryA.kt +++ /dev/null @@ -1,153 +0,0 @@ -package io.github.notenoughupdates.moulconfig.test - -import com.google.gson.annotations.Expose -import io.github.notenoughupdates.moulconfig.ChromaColour -import io.github.notenoughupdates.moulconfig.annotations.* -import io.github.notenoughupdates.moulconfig.observer.Property -import org.lwjgl.glfw.GLFW -import java.util.* - -class TestCategoryA { - - @Expose - @ConfigOrder(Integer.MAX_VALUE) - @ConfigOption(name = "Bottom option!", desc = "Declared at top, but should appear at the bottom by Order annotation.") - @ConfigEditorInfoText(infoTitle = "Bottom option") - var bottomOption: Boolean = false - - @ConfigOption(name ="Open Wide", desc= "Use a wider config menu") - @ConfigEditorBoolean - var isWide: Boolean = false - @ConfigOption(name = "Test Option", desc = "Test toggle") - @ConfigEditorBoolean - var shouldTestToggle: Boolean = false - - @ConfigOption(name = "Pronouns in MoulConfig", desc = "Fuck It") - @ConfigEditorDropdown - var dropdownTest: Pronouns = Pronouns.ITITS - - enum class Pronouns(val label: String) { - HEHIM("He/Him"), - SHEHER("She/Her"), - ITITS("It/Its"), - THEYTHEM("They/Them"), - USE_NAME("Use Name"), - ; - - override fun toString(): String { - return label - } - } - - - @Accordion - @ConfigOption(name = "Accordion", desc = "") - var accordion = AccordionClass() - - class AccordionClass() { - - @ConfigOption(name = "Number Dropdown", desc = "0, 1, 2, 3") - @ConfigEditorDropdown(values = ["0", "1", "2", "3"]) - var numberDropdown: Int = 0 - - @Expose - @ConfigOption(name = "Drop Down", desc = "Using strings") - @ConfigEditorDropdown(values = ["A", "B", "C"]) - var dropDownUisngStrings: String = "A" - - - @ConfigOption(name = "Enum Dropdown", desc = "1, 2, 3, 4") - @ConfigEditorDropdown - var enumDropdown: Property = Property.of(DropdownEnum.FOUR) - } - - enum class DropdownEnum(private val label: String) { - ONE("1"), - TWO("2"), - THREE("3"), - FOUR("4"), - ; - - override fun toString(): String { - return label - } - } - - @ConfigOption(name = "Slider", desc = "Between 1 and 5") - @ConfigEditorSlider(minValue = 1F, maxValue = 5f, minStep = 1F) - var minimumTitle: Int = 1 - - @ConfigOption(name = "Info Box", desc = "Shows important info to the user") - @ConfigEditorInfoText( - infoTitle = "§cAlert", - ) - var notice: String = "" - - @ConfigOption(name = "Text Box", desc = "Lets you put strings.") - @ConfigEditorText(forbidden = "§z") - var customText: Property = Property.of("abc") - - @ConfigOption(name = "Draggable List", desc = "§eDrag text to change the order of the list.") - @ConfigEditorDraggableList( - exampleText = ["abc", "dec", "blah", "surel it works really cool and great :))"] - ) - var draggableList: List = ArrayList(mutableListOf(0, 1, 2, 3)) - - @Expose - @ConfigOption(name = "Enum Draggable List", desc = "Draggable list but doesnt work properly.") - @ConfigEditorDraggableList - var enumDraggableList: List = ArrayList( - Arrays.asList( - EnumDraggableList.ONE, - EnumDraggableList.THREE, - EnumDraggableList.TWO, - ) - ) - - enum class EnumDraggableList(private val str: String) { - ONE("1"), - TWO("too"), - THREE("three"), - ; - - override fun toString(): String { - return str - } - } - - @ConfigOption(name = "Colour Test", desc = "Test a colour editor") - @ConfigEditorColour - var colour = ChromaColour(0F, 1f, 1f, 0, 0xFF) - - @Expose - @ConfigOption(name = "Keybind", desc = "The Number One") - @ConfigEditorKeybind(defaultKey = GLFW.GLFW_KEY_1) - var slot1: Int = GLFW.GLFW_KEY_1 - - - @Expose - @ConfigOption(name = "Test Runnable", desc = "Test a java.lang.Runnable") - @ConfigEditorButton(buttonText = "Click me") - val runnable = Runnable { - println("JRunnable working") - } - @Expose - @ConfigOption(name = "Test Runnable", desc = "Test a kotlin.jvm.functions.Function0") - @ConfigEditorButton(buttonText = "Click me") - val kRunnable = { - println("KFunction0 working") - } - @Expose - @ConfigOption(name = "Test Runnable", desc = "Test a (ignored) runnable using runnableId to emit a test warning") - @ConfigEditorButton( - runnableId = 10, - buttonText = "Click me") - val runnableId = Unit - - @Expose - @ConfigOrder(-1) - @ConfigOption(name = "Top option!", desc = "Declared at the bottom, floated to top by Order annotation.") - @ConfigEditorInfoText(infoTitle = "Top option") - var topOption: Boolean = false - -} diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.java b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.java new file mode 100644 index 000000000..03413112c --- /dev/null +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.java @@ -0,0 +1,8 @@ +package io.github.notenoughupdates.moulconfig.test; + +import io.github.notenoughupdates.moulconfig.annotations.Category; + +public class TestCategoryB { + @Category(name = "Test Inner Category", desc = "") + public InnerCategory innerCategory = new InnerCategory(); +} diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.kt b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.kt deleted file mode 100644 index 57ce1bcbe..000000000 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestCategoryB.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.notenoughupdates.moulconfig.test - -import io.github.notenoughupdates.moulconfig.annotations.Category - -class TestCategoryB { - - @Category(name = "Test Inner Category", desc = "") - var innerCategory: InnerCategory = InnerCategory() - -} diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.java b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.java new file mode 100644 index 000000000..fc76463da --- /dev/null +++ b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.java @@ -0,0 +1,31 @@ +package io.github.notenoughupdates.moulconfig.test; + +import io.github.notenoughupdates.moulconfig.Config; +import io.github.notenoughupdates.moulconfig.annotations.Category; +import io.github.notenoughupdates.moulconfig.common.text.StructuredText; + +public class TestConfig extends Config { + @Override + public StructuredText getTitle() { + return StructuredText.of("1.20 Test").green(); + } + + @Override + public boolean isValidRunnable(int runnableId) { + return false; + } + + @Category(name = "Cat a", desc = "Cat a desc") + public TestCategoryA testCategoryA = new TestCategoryA(); + + @Category(name = "Cat b", desc = "Cat b desc") + public TestCategoryB testCategoryB = new TestCategoryB(); + + public TestCategoryA getTestCategoryA() { + return testCategoryA; + } + + public TestCategoryB getTestCategoryB() { + return testCategoryB; + } +} diff --git a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.kt b/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.kt deleted file mode 100644 index 684292fe4..000000000 --- a/modern/templates/java/io/github/notenoughupdates/moulconfig/test/TestConfig.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.notenoughupdates.moulconfig.test - -import io.github.notenoughupdates.moulconfig.Config -import io.github.notenoughupdates.moulconfig.annotations.Category -import io.github.notenoughupdates.moulconfig.common.text.StructuredText - -class TestConfig : Config() { - override fun getTitle(): StructuredText { - return StructuredText.of("1.20 Test").green() - } - - override fun isValidRunnable(runnableId: Int): Boolean { - return false - } - - @Category(name = "Cat a", desc = "Cat a desc") - var testCategoryA: TestCategoryA = TestCategoryA() - - @Category(name = "Cat b", desc = "Cat b desc") - var testCategoryB: TestCategoryB = TestCategoryB() -}