diff --git a/build.gradle b/build.gradle deleted file mode 100644 index f06e2fc1..00000000 --- a/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -buildscript { - repositories { - jcenter() - maven { url = "http://files.minecraftforge.net/maven" } - maven { url "https://plugins.gradle.org/m2/" } - } - dependencies { - classpath('com.anatawa12.forge:ForgeGradle:2.3-1.0.+') { - changing = true - } - } -} - -apply plugin: 'net.minecraftforge.gradle.forge' - -def ENV = System.getenv() -version = ENV.BUILD_NUMBER ? "${mod_version}.${ENV.BUILD_NUMBER}" : "${mod_version}.2" -group = package_group -archivesBaseName = mod_name - -sourceCompatibility = targetCompatibility = "1.8" -compileJava { - sourceCompatibility = targetCompatibility = "1.8" -} - -minecraft { - version = "1.12.2-14.23.5.2838" - runDir = "run" - mappings = "${mappings_version}" - - replace "0.0.0.ftblib", project.version - replaceIn "${mod_reference}" -} - -dependencies { - compileOnly fileTree(dir: project.lib, include: ['*.jar'], exclude: ['*-sources.jar']) -} - -processResources { - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version - - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version': project.version, 'mcversion': project.minecraft.version - } - - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } -} - -jar { - manifest { - attributes 'FMLAT': 'ftblib_at.cfg' - } -} - -artifacts { - archives sourceJar -} - -tasks.register('deobfJar', Jar) { - from sourceSets.main.output - archiveClassifier.set('deobf') -} - -build.finalizedBy deobfJar diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..08a587df --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,320 @@ +import org.jetbrains.gradle.ext.Gradle +import org.jetbrains.gradle.ext.RunConfigurationContainer +import java.util.* + +plugins { + id("java-library") + id("maven-publish") + id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.9" + id("eclipse") + id("com.gtnewhorizons.retrofuturagradle") version "1.4.1" +} + +// Project properties +group = project.findProperty("package_group") as? String ?: "" +version = project.findProperty("mod_version") as? String ?: "" + +// Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod +java { +// toolchain { +// languageVersion.set(JavaLanguageVersion.of(17)) +// // Azul covers the most platforms for Java 8 toolchains, crucially including MacOS arm64 +// vendor.set(JvmVendorSpec.AZUL) +// } + // Generate sources and javadocs jars when building and publishing + withSourcesJar() + withJavadocJar() +} + +// Most RFG configuration lives here, see the JavaDoc for com.gtnewhorizons.retrofuturagradle.MinecraftExtension +minecraft { + mcVersion.set("1.12.2") + + // Username for client run configurations + username.set("Dev") + + // Generate a field named VERSION with the mod version in the injected Tags class + injectedTags.put("VERSION", project.version) + + // If you need the old replaceIn mechanism, prefer the injectTags task because it doesn't inject a javac plugin. + // tagReplacementFiles.add("RfgExampleMod.java") + + // Enable assertions in the mod's package when running the client or server + val args = mutableListOf("-ea:${project.group}") + + // Mixin args + args.add("-Dmixin.hotSwap=true") + args.add("-Dmixin.checks.interfaces=true") + args.add("-Dmixin.debug.export=true") + extraRunJvmArguments.addAll(args) + + // If needed, add extra tweaker classes like for mixins. + // extraTweakClasses.add("org.spongepowered.asm.launch.MixinTweaker") + + // Exclude some Maven dependency groups from being automatically included in the reobfuscated runs + groupsToExcludeFromAutoReobfMapping.addAll("com.diffplug", "com.diffplug.durian", "net.industrial-craft") +} + +// Generates a class named rfg.examplemod.Tags with the mod version in it, you can find it at +tasks.injectTags.configure { + outputClassName.set("${project.group}.Tags") +} + +// Put the version from gradle into mcmod.info +tasks.processResources.configure { +// inputs.property("version", project.version) +// +// filesMatching("mcmod.info") { +// expand(mapOf("version" to project.version)) +// } +} + +tasks.compileJava.configure { +// sourceCompatibility = "17" +// options.release = 8 + options.encoding = "UTF-8" // Use the UTF-8 charset for Java compilation + +// javaCompiler = javaToolchains.compilerFor { +// languageVersion = JavaLanguageVersion.of(17) +// } +} + +tasks.compileTestJava.configure { +// sourceCompatibility = "17" +// options.release = 8 + options.encoding = "UTF-8" // Use the UTF-8 charset for Java compilation + +// javaCompiler = javaToolchains.compilerFor { +// languageVersion = JavaLanguageVersion.of(17) +// } +} + +tasks.javadoc.configure { + // No need for JavaDoc. + actions = Collections.emptyList() +} + +tasks.jar.configure { + archiveBaseName.set(project.name) + manifest { + val attributes = manifest.attributes + attributes["FMLAT"] = "ftblib_at.cfg" + } +} + +tasks.deobfuscateMergedJarToSrg.configure { + accessTransformerFiles.from("src/main/resources/META-INF/ftblib_at.cfg") +} +tasks.srgifyBinpatchedJar.configure { + accessTransformerFiles.from("src/main/resources/META-INF/ftblib_at.cfg") +} + +// Create a new dependency type for runtime-only dependencies that don't get included in the maven publication +val runtimeOnlyNonPublishable: Configuration by configurations.creating { + description = "Runtime only dependencies that are not published alongside the jar" + isCanBeConsumed = false + isCanBeResolved = false +} +listOf(configurations.runtimeClasspath, configurations.testRuntimeClasspath).forEach { + it.configure { + extendsFrom( + runtimeOnlyNonPublishable + ) + } +} + +// Dependencies +repositories { + flatDir { + dirs("lib") + } + maven { + url = uri("https://maven.aliyun.com/nexus/content/groups/public/") + } + maven { + url = uri("https://maven.aliyun.com/nexus/content/repositories/jcenter") + } + maven { + url = uri("https://maven.cleanroommc.com") + } + maven { + url = uri("https://cfa2.cursemaven.com") + } + maven { + url = uri("https://cursemaven.com") + } + maven { + url = uri("https://maven.blamejared.com/") + } + maven { + url = uri("https://repo.spongepowered.org/maven") + } + maven { + name = "GeckoLib" + url = uri("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/") + } + maven { + name = "OvermindDL1 Maven" + url = uri("https://gregtech.overminddl1.com/") + mavenContent { + excludeGroup("net.minecraftforge") // missing the `universal` artefact + } + } + maven { + name = "GTNH Maven" + url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/") + isAllowInsecureProtocol = true + } +} + +dependencies { +// annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2") +// compileOnly("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2") +// // workaround for https://github.com/bsideup/jabel/issues/174 +// annotationProcessor("net.java.dev.jna:jna-platform:5.13.0") +// // Allow jdk.unsupported classes like sun.misc.Unsafe, workaround for JDK-8206937 and fixes Forge crashes in tests. +// patchedMinecraft("me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0") +// // allow Jabel to work in tests +// testAnnotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:1.0.0") +// testCompileOnly("com.github.bsideup.jabel:jabel-javac-plugin:1.0.0") { +// isTransitive = false // We only care about the 1 annotation class +// } +// testCompileOnly("me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0") + + // Mixins +// implementation("zone.rong:mixinbooter:7.1") +// val mixin : String = modUtils.enableMixins("zone.rong:mixinbooter:8.9", "mixins.stellar_core.refmap.json").toString() +// api (mixin) { +// isTransitive = false +// } +// annotationProcessor("org.ow2.asm:asm-debug-all:5.2") +// annotationProcessor("com.google.guava:guava:30.0-jre") +// annotationProcessor("com.google.code.gson:gson:2.8.9") +// annotationProcessor (mixin) { +// isTransitive = false +// } + + // Mod Dependencies +// implementation("com.cleanroommc:configanytime:2.0") +// implementation("CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.+") +// compileOnly(rfg.deobf("curse.maven:modularmachinery-community-edition-817377:4991928")) + implementation(rfg.deobf("curse.maven:had-enough-items-557549:5210315")) + + implementation("org.mongodb:bson:3.12.10@jar") + implementation("org.mongodb:mongodb-driver-core:3.12.10@jar") + implementation("org.mongodb:mongodb-driver-sync:3.12.10@jar") + implementation("org.slf4j:slf4j-api:1.7.25") + +// implementation(rfg.deobf("curse.maven:the-one-probe-245211:2667280")) +// compileOnly(rfg.deobf("curse.maven:ae2-extended-life-570458:5147702")) +// compileOnly(rfg.deobf("curse.maven:CodeChickenLib-242818:2779848")) +// compileOnly(rfg.deobf("curse.maven:nuclearcraft-overhauled-336895:3862197")) +// compileOnly(rfg.deobf("curse.maven:industrialcraft-2-242638:3078604")) +// compileOnly(rfg.deobf("curse.maven:mekanism-ce-unofficial-840735:5130458")) +// compileOnly(rfg.deobf("curse.maven:mekanism-unofficial-edition-v10-edition-840735:4464199")) +// compileOnly(rfg.deobf("curse.maven:RedstoneFlux-270789:2920436")) +// compileOnly(rfg.deobf("curse.maven:cofh-core-69162:2920433")) +// compileOnly(rfg.deobf("curse.maven:cofh-world-271384:2920434")) +// compileOnly(rfg.deobf("curse.maven:thermal-foundation-222880:2926428")) +// compileOnly(rfg.deobf("curse.maven:thermal-innovation-291737:2920441")) +// compileOnly(rfg.deobf("curse.maven:thermal-expansion-69163:2926431")) +// compileOnly(rfg.deobf("curse.maven:botania-225643:3330934")) +// compileOnly(rfg.deobf("curse.maven:astral-sorcery-241721:3044416")) +// compileOnly(rfg.deobf("curse.maven:baubles-227083:2518667")) +// compileOnly(rfg.deobf("curse.maven:zenutil-401178:4394263")) +// compileOnly(rfg.deobf("curse.maven:immersive-engineering-231951:2974106")) +// compileOnly(rfg.deobf("curse.maven:immersive-petroleum-268250:3382321")) +// compileOnly(rfg.deobf("curse.maven:smooth-font-285742:3944565")) +// compileOnly(rfg.deobf("curse.maven:athenaeum-284350:4633750")) +// compileOnly(rfg.deobf("curse.maven:artisan-worktables-284351:3205284")) +// compileOnly(rfg.deobf("curse.maven:touhou-little-maid-355044:3576415")) +// compileOnly(rfg.deobf("curse.maven:ingame-info-xml-225604:2489566")) +// compileOnly(rfg.deobf("curse.maven:lunatriuscore-225605:2489549")) +// compileOnly(rfg.deobf("curse.maven:rgb-chat-702720:4092100")) +// compileOnly(rfg.deobf("curse.maven:endercore-231868:4671384")) +// compileOnly(rfg.deobf("curse.maven:ender-io-64578:4674244")) +// compileOnly(rfg.deobf("curse.maven:tinkers-evolution-384589:4941753")) +// compileOnly(rfg.deobf("curse.maven:ore-excavation-250898:2897369")) +// compileOnly(rfg.deobf("curse.maven:techguns-244201:2958103")) +// compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:3558882")) +// compileOnly(rfg.deobf("curse.maven:more-electric-tools-366298:3491973")) +// compileOnly(rfg.deobf("curse.maven:brandonscore-231382:3051539")) +// compileOnly(rfg.deobf("curse.maven:draconicevolution-223565:3051542")) +// compileOnly(rfg.deobf("curse.maven:mantle-74924:2713386")) +// compileOnly(rfg.deobf("curse.maven:tinkers-construct-74072:2902483")) +// compileOnly(rfg.deobf("curse.maven:thermal-dynamics-227443:2920505")) +// compileOnly(rfg.deobf("curse.maven:armourers-workshop-229523:3101995")) +// compileOnly(rfg.deobf("curse.maven:avaritia-1-10-261348:3143349")) +// compileOnly(rfg.deobf("curse.maven:blood-magic-224791:2822288")) +// compileOnly(rfg.deobf("curse.maven:legendary-tooltips-532127:4499615")) +// compileOnly(rfg.deobf("curse.maven:ftb-quests-forge-289412:3156637")) +// compileOnly(rfg.deobf("curse.maven:flux-networks-248020:3178199")) +// compileOnly(rfg.deobf("curse.maven:scalingguis-319656:2716334")) +// compileOnly(rfg.deobf("curse.maven:chisel-235279:2915375")) +// compileOnly(rfg.deobf("curse.maven:extrabotany-299086:3112313")) +// compileOnly(rfg.deobf("curse.maven:better-loading-screen-229302:3769828")) +// compileOnly(rfg.deobf("curse.maven:better-chat-363860:3048407")) +// compileOnly(rfg.deobf("curse.maven:mrcrayfish-furniture-mod-55438:3865259")) +// compileOnly(rfg.deobf("curse.maven:cucumber-272335:2645867")) +// compileOnly(rfg.deobf("curse.maven:sync-229090:2682824")) +// compileOnly(rfg.deobf("curse.maven:libvulpes-236541:3801015")) +// compileOnly(rfg.deobf("curse.maven:advanced-rocketry-236542:4671856")) +// compileOnly(rfg.deobf("curse.maven:gugu-utils-530919:3652765")) +// compileOnly(rfg.deobf("curse.maven:not-enough-energistics-515565:4690660")) +// compileOnly(rfg.deobf("curse.maven:avaritiaddons-248873:4745387")) +// compileOnly(rfg.deobf("curse.maven:custom-starter-gear-253735:2514705")) +// compileOnly(rfg.deobf("curse.maven:modular-routers-250294:2954953")) +// compileOnly(rfg.deobf("curse.maven:ftb-library-legacy-forge-237167:2985811")) +// compileOnly(rfg.deobf("curse.maven:item-filters-309674:3003364")) +// compileOnly(rfg.deobf("curse.maven:ftb-quests-forge-289412:3156637")) +} + +// IDE Settings +//eclipse { +// classpath { +// isDownloadSources = true +// isDownloadJavadoc = true +// } +//} + +idea { + module { + isDownloadJavadoc = true + isDownloadSources = true + inheritOutputDirs = true // Fix resources in IJ-Native runs + } + project { + this.withGroovyBuilder { + "settings" { + "runConfigurations" { + val self = this.delegate as RunConfigurationContainer + self.add(Gradle("1. Run Client").apply { + setProperty("taskNames", listOf("runClient")) + }) + self.add(Gradle("2. Run Server").apply { + setProperty("taskNames", listOf("runServer")) + }) + self.add(Gradle("3. Run Obfuscated Client").apply { + setProperty("taskNames", listOf("runObfClient")) + }) + self.add(Gradle("4. Run Obfuscated Server").apply { + setProperty("taskNames", listOf("runObfServer")) + }) + } + "compiler" { + val self = this.delegate as org.jetbrains.gradle.ext.IdeaCompilerConfiguration + afterEvaluate { + self.javac.moduleJavacAdditionalOptions = mapOf( + (project.name + ".main") to + tasks.compileJava.get().options.compilerArgs.map { '"' + it + '"' }.joinToString(" ") + ) + } + } + } + } + } +} + +tasks.processIdeaSettings.configure { + dependsOn(tasks.injectTags) +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index edcf837b..1c36ffc2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,3 +6,4 @@ mod_reference=FTBLib.java mc_version=1.12.2 mappings_version=snapshot_20180814 lib=lib + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7742042d..a88229ef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip + diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..e8306988 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + maven { + // RetroFuturaGradle + name = "GTNH Maven" + url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/") + isAllowInsecureProtocol = true + mavenContent { + includeGroup("com.gtnewhorizons") + includeGroup("com.gtnewhorizons.retrofuturagradle") + } + } + gradlePluginPortal() + mavenCentral() + mavenLocal() + } +} + +plugins { + // Automatic toolchain provisioning + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" +} + +rootProject.name = "FTB-Library" \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/Theme.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/Theme.java index baefeb89..ed4c919f 100644 --- a/src/main/java/com/feed_the_beast/ftblib/lib/gui/Theme.java +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/Theme.java @@ -34,6 +34,9 @@ public class Theme { private static final Color4I CONTENT_COLOR_MOUSE_OVER = Color4I.rgb(16777120); private static final Color4I CONTENT_COLOR_DISABLED = Color4I.rgb(10526880); private static final Color4I CONTENT_COLOR_DARK = Color4I.rgb(4210752); + private static final Color4I COLOR_LINK_HOVER = Color4I.LIGHT_GREEN; + private static final Color4I COLOR_LINK = Color4I.LIGHT_BLUE; + public static final ImageIcon BACKGROUND_SQUARES = (ImageIcon) Icon.getIcon(FTBLib.MOD_ID + ":textures/gui/background_squares.png"); private static final ImageIcon TEXTURE_BEACON = (ImageIcon) Icon.getIcon("textures/gui/container/beacon.png"); @@ -65,6 +68,14 @@ public class Theme { private final BooleanStack fontUnicode = new BooleanArrayList(); + public Color4I getColorLink() { + return COLOR_LINK; + } + + public Color4I getColorLinkHover() { + return COLOR_LINK_HOVER; + } + public Color4I getContentColor(WidgetType type) { return type == WidgetType.MOUSE_OVER ? CONTENT_COLOR_MOUSE_OVER : type == WidgetType.DISABLED ? CONTENT_COLOR_DISABLED : Color4I.WHITE; } diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/Alignment.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/Alignment.java new file mode 100644 index 00000000..67d51d53 --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/Alignment.java @@ -0,0 +1,7 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +public enum Alignment { + LEFT, + RIGHT, + CENTER +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/BaseMarkdownElementWidget.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/BaseMarkdownElementWidget.java new file mode 100644 index 00000000..4550ff10 --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/BaseMarkdownElementWidget.java @@ -0,0 +1,32 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Widget; + +public abstract class BaseMarkdownElementWidget extends Widget implements IMarkdownElement { + protected Alignment alignment = Alignment.LEFT; + + public BaseMarkdownElementWidget(Panel panel) { + super(panel); + } + + @Override + public Alignment getAlignment() { + return this.alignment; + } + + @Override + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } + + @Override + public int getWidth() { + return this.width; + } + + @Override + public int getHeight() { + return this.height; + } +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/IMarkdownElement.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/IMarkdownElement.java new file mode 100644 index 00000000..cb6a2aad --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/IMarkdownElement.java @@ -0,0 +1,13 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +public interface IMarkdownElement { + int getX(); + int getY(); + void setX(int x); + void setY(int y); + int getWidth(); + int getHeight(); + Alignment getAlignment(); + void setAlignment(Alignment alignment); + +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/ImageWidget.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/ImageWidget.java new file mode 100644 index 00000000..80b2cca8 --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/ImageWidget.java @@ -0,0 +1,64 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Theme; +import com.feed_the_beast.ftblib.lib.icon.Color4I; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.util.ResourceLocation; + +import static com.feed_the_beast.ftblib.lib.gui.GuiHelper.drawTexturedRect; + +public class ImageWidget extends BaseMarkdownElementWidget { + public final String altText; + public final String url; + public ResourceLocation resourceLocation; + public boolean isLoaded; + + public ImageWidget(Panel panel, String altText, String url) { + super(panel); + this.altText = altText; + this.setWidth(32); + this.setHeight(32); + this.isLoaded = false; + this.url = url; + + if (this.url.startsWith("minecraft:")) { + this.resourceLocation = new ResourceLocation(this.url); + this.isLoaded = true; + } + } + + @Override + public void draw(Theme theme, int x, int y, int w, int h) { + int imgX = x + posX; + int imgY = y + posY; + + if (isLoaded) { + TextureManager textureManager = Minecraft.getMinecraft().getTextureManager(); + GlStateManager.pushMatrix(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + textureManager.bindTexture(resourceLocation); + drawTexturedRect(imgX, imgY, width, height, Color4I.WHITE, 0.0, 0.0, 1.0, 1.0); + GlStateManager.popMatrix(); + } else { + Color4I.GRAY.withAlpha(80).draw(imgX, imgY, width, height); + theme.drawString(altText, imgX + 2, imgY + height / 2 - 4, Color4I.WHITE, 0); + } + } + + public void align(int contentWidth) { + switch (alignment) { + case LEFT: + posX = 0; + break; + case CENTER: + posX = (contentWidth - width) / 2; + break; + case RIGHT: + posX = contentWidth - width; + break; + } + } +} diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LineWidget.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LineWidget.java new file mode 100644 index 00000000..565be87b --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LineWidget.java @@ -0,0 +1,154 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.BlankPanel; +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Theme; +import net.minecraft.client.renderer.GlStateManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class LineWidget extends BlankPanel { + private static final Pattern LINK_PATTERN = Pattern.compile("(? tokens; + + public LineWidget(Panel panel, String text) { + super(panel); + this.text = text; + this.tokens = new ArrayList<>(); + this.alignment = Alignment.LEFT; + + Theme theme = panel.getGui().getTheme(); + this.setHeight(theme.getFontHeight() + 1); + + parseText(); + } + + public void align(int contentWidth) { + switch (alignment) { + case LEFT: + posX = 0; + break; + case CENTER: + posX = (contentWidth - width) / 2; + break; + case RIGHT: + posX = contentWidth - width; + break; + } + } + + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } + + public Alignment getAlignment() { + return alignment; + } + + private void parseText() { + if (text == null || text.isEmpty()) { + return; + } + + Theme theme = getGui().getTheme(); + + // Find all links in the text + Matcher matcher = LINK_PATTERN.matcher(text); + List links = new ArrayList<>(); + + while (matcher.find()) { + String linkText = matcher.group(1); + String url = matcher.group(2); + links.add(new LinkInfo(matcher.start(), matcher.end(), linkText, url)); + } + + // If no links found, add the whole text as a single token + if (links.isEmpty()) { + TokenWidget token = new TokenWidget(this, text); + tokens.add(token); + setWidth(theme.getStringWidth(text)); + return; + } + + // Process text with links + int currentX = 0; + int lastEnd = 0; + + for (LinkInfo link : links) { + // Add normal text token before the link + if (link.start > lastEnd) { + String normalText = text.substring(lastEnd, link.start); + TokenWidget normalToken = new TokenWidget(this, normalText); + normalToken.setX(currentX); + tokens.add(normalToken); + currentX += normalToken.getWidth(); + } + + // Add link token + LinkTokenWidget linkToken = new LinkTokenWidget(this, link.text, link.url); + linkToken.setX(currentX); + tokens.add(linkToken); + currentX += linkToken.getWidth(); + + lastEnd = link.end; + } + + // Add remaining text after the last link + if (lastEnd < text.length()) { + String remainingText = text.substring(lastEnd); + TokenWidget remainingToken = new TokenWidget(this, remainingText); + remainingToken.setX(currentX); + tokens.add(remainingToken); + currentX += remainingToken.getWidth(); + } + + // Set the total width of the line + setWidth(currentX); + addWidgets(); + } + + @Override + public void draw(Theme theme, int x, int y, int w, int h) { + GlStateManager.pushMatrix(); + GlStateManager.translate(x + posX, y + posY, 0); + + // Draw all tokens + for (BaseMarkdownElementWidget token : tokens) { + token.draw(theme, 0, 0, w, h); + } + GlStateManager.popMatrix(); + + } + + @Override + public void addWidgets() { + widgets.clear(); + + // Добавляем токены как дочерние виджеты + for (BaseMarkdownElementWidget token : tokens) { + add(token); + } + } + + /** + * Helper class to store link information + */ + private static class LinkInfo { + int start; + int end; + String text; + String url; + + LinkInfo(int start, int end, String text, String url) { + this.start = start; + this.end = end; + this.text = text; + this.url = url; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LinkTokenWidget.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LinkTokenWidget.java new file mode 100644 index 00000000..386361f7 --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/LinkTokenWidget.java @@ -0,0 +1,75 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Theme; +import com.feed_the_beast.ftblib.lib.icon.Color4I; +import com.feed_the_beast.ftblib.lib.util.misc.MouseButton; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiConfirmOpenLink; +import net.minecraft.client.gui.GuiYesNoCallback; +import net.minecraft.client.renderer.GlStateManager; + +import java.net.URI; + +public class LinkTokenWidget extends TokenWidget implements GuiYesNoCallback { + private final String url; + Minecraft mc = Minecraft.getMinecraft(); + + public LinkTokenWidget(Panel panel, String text, String url) { + super(panel, text); + this.url = url; + } + + public String getUrl() { + return url; + } + + @Override + public void draw(Theme theme, int x, int y, int w, int h) { + GlStateManager.pushMatrix(); + GlStateManager.translate(x + posX, y + posY, 0); + + Color4I currentColor = isMouseOver ? theme.getColorLinkHover() : theme.getColorLink(); + + theme.drawString(text, 0, 0, currentColor, textFlags); + + GlStateManager.popMatrix(); + } + + @Override + public boolean mousePressed(MouseButton button) { + if (button.isLeft()) { + openLink(); + return true; + } + return false; + } + + private void openLink() { + GuiConfirmOpenLink guiconfirmopenlink = new GuiConfirmOpenLink(this, this.url, 13, true); + guiconfirmopenlink.disableSecurityWarning(); + mc.displayGuiScreen(guiconfirmopenlink); + } + + public void confirmClicked(boolean result, int id) + { + if (id == 13) + { + if (result) + { + try + { + Class oclass = Class.forName("java.awt.Desktop"); + Object object = oclass.getMethod("getDesktop").invoke(null); + oclass.getMethod("browse", URI.class).invoke(object, new URI(this.url)); + } + catch (Throwable ignored) + { + + } + } + + this.mc.displayGuiScreen(this.parent.getGui().getPrevScreen()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/MarkdownPanel.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/MarkdownPanel.java new file mode 100644 index 00000000..01a0f389 --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/MarkdownPanel.java @@ -0,0 +1,259 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.BlankPanel; +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Theme; +import com.feed_the_beast.ftblib.lib.gui.Widget; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MarkdownPanel extends BlankPanel { + private static final Pattern IMAGE_PATTERN = Pattern.compile("!\\[(.*?)\\]\\((.*?)\\)(\\{w=(\\d+)\\s*h=(\\d+)\\})?"); + private static final Pattern ALIGNMENT_PATTERN = Pattern.compile("^\\[\\[(center|left|right)\\]\\](.*)$"); + private static final Pattern BLOCK_PATTERN = Pattern.compile("<(center|left|right)>(.*?)", Pattern.DOTALL); + private static final Pattern LINK_PATTERN = Pattern.compile("(? 0 ? maxWidth : width; + Theme theme = getGui().getTheme(); + + if (markdown == null || markdown.trim().isEmpty()) { + return resize(theme); + } + + markdown = markdown.trim(); + String processedMarkdown = processBlockAlignments(markdown); + renderMarkdownContent(processedMarkdown, contentWidth, theme); + + return resize(theme); + } + + private String processBlockAlignments(String markdown) { + Matcher blockMatcher = BLOCK_PATTERN.matcher(markdown); + StringBuffer processedMarkdown = new StringBuffer(); + + while (blockMatcher.find()) { + String alignment = blockMatcher.group(1); + String content = blockMatcher.group(2); + + String[] contentLines = content.split("\n"); + StringBuilder markedContent = new StringBuilder(); + + for (String line : contentLines) { + if (!line.trim().isEmpty()) { + markedContent.append("[[").append(alignment).append("]]").append(line).append("\n"); + } else { + markedContent.append("\n"); + } + } + + blockMatcher.appendReplacement(processedMarkdown, markedContent.toString()); + } + blockMatcher.appendTail(processedMarkdown); + + return processedMarkdown.toString(); + } + + private void renderMarkdownContent(String processedMarkdown, int contentWidth, Theme theme) { + String[] lines = processedMarkdown.split("\n"); + int currentY = 0; + + for (String line : lines) { + LineInfo lineInfo = extractLineAlignment(line); + + if (isImageLine(lineInfo.text)) { + currentY = processImageLine(lineInfo.text, lineInfo.alignment, contentWidth, currentY); + } else if (!lineInfo.text.trim().isEmpty()) { + currentY = processTextLine(lineInfo.text, lineInfo.alignment, contentWidth, currentY, theme); + } else { + currentY += theme.getFontHeight(); + } + } + } + + private LineInfo extractLineAlignment(String line) { + Alignment alignment = Alignment.LEFT; + String text = line; + + Matcher alignmentMatcher = ALIGNMENT_PATTERN.matcher(line); + if (alignmentMatcher.matches()) { + String alignmentType = alignmentMatcher.group(1); + text = alignmentMatcher.group(2); + + switch (alignmentType) { + case "center": + alignment = Alignment.CENTER; + break; + case "right": + alignment = Alignment.RIGHT; + break; + default: + alignment = Alignment.LEFT; + break; + } + } + + return new LineInfo(text, alignment); + } + + private boolean isImageLine(String line) { + return IMAGE_PATTERN.matcher(line).matches(); + } + + private int processImageLine(String line, Alignment alignment, int contentWidth, int currentY) { + Matcher imageMatcher = IMAGE_PATTERN.matcher(line); + if (imageMatcher.matches()) { + String altText = imageMatcher.group(1); + String url = imageMatcher.group(2); + + int width = -1; + int height = -1; + + if (imageMatcher.group(3) != null) { + try { + width = Integer.parseInt(imageMatcher.group(4)); + height = Integer.parseInt(imageMatcher.group(5)); + } catch (NumberFormatException e) { + // ;3 + } + } + + ImageWidget imageWidget = new ImageWidget(this, altText, url); + imageWidget.setAlignment(alignment); + + if (width > 0 && height > 0) { + imageWidget.setWidth(width); + imageWidget.setHeight(height); + } + + imageWidget.align(contentWidth); + imageWidget.setY(currentY); + + add(imageWidget); + return currentY + imageWidget.getHeight() + imageMargin; + } + + return currentY; + } + + private int processTextLine(String text, Alignment alignment, int contentWidth, int currentY, Theme theme) { + // Oh no + String textForFormatting = preprocessLinksForWidth(text); + + List formattedLines = maxWidth > 0 + ? theme.listFormattedStringToWidth(textForFormatting, maxWidth) + : Collections.singletonList(textForFormatting); + + int newY = currentY; + + for (String formattedLine : formattedLines) { + // Anyway + String originalLine = restoreLinksInLine(formattedLine, text); + + LineWidget lineWidget = new LineWidget(this, originalLine); + lineWidget.setAlignment(alignment); + lineWidget.align(contentWidth); + lineWidget.setY(newY); + + add(lineWidget); + newY += theme.getFontHeight(); + } + + return newY; + } + + private String preprocessLinksForWidth(String text) { + StringBuffer result = new StringBuffer(); + Matcher matcher = LINK_PATTERN.matcher(text); + + while (matcher.find()) { + String linkText = matcher.group(1); + matcher.appendReplacement(result, linkText); + } + matcher.appendTail(result); + + return result.toString(); + } + + private String restoreLinksInLine(String formattedLine, String originalText) { + Map linkTextToFullLink = new HashMap<>(); + Matcher linkMatcher = LINK_PATTERN.matcher(originalText); + + while (linkMatcher.find()) { + String linkText = linkMatcher.group(1); + String fullLink = linkMatcher.group(0); // [text](link) + linkTextToFullLink.put(linkText, fullLink); + } + + String result = formattedLine; + for (Map.Entry entry : linkTextToFullLink.entrySet()) { + result = result.replace(entry.getKey(), entry.getValue()); + } + + return result; + } + + public MarkdownPanel resize(Theme theme) { + if (widgets.isEmpty()) { + setWidth(0); + setHeight(0); + return this; + } + + int maxLineWidth = 0; + int maxY = 0; + + for (Widget widget : widgets) { + maxLineWidth = Math.max(maxLineWidth, widget.getX() + widget.width + imageMargin); + maxY = Math.max(maxY, widget.getY() + widget.height + imageMargin); + } + + setWidth(maxWidth > 0 ? maxWidth : maxLineWidth); + setHeight(maxY); + + return this; + } + + @Override + public void draw(Theme theme, int x, int y, int w, int h) { + parent.drawBackground(theme, x, y, w, h); + + if (widgets.isEmpty()) { + return; + } + + for (Widget widget : widgets) { + widget.draw(theme, x, y, w, h); + } + } + + private static class LineInfo { + final String text; + final Alignment alignment; + + LineInfo(String text, Alignment alignment) { + this.text = text; + this.alignment = alignment; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/TokenWidget.java b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/TokenWidget.java new file mode 100644 index 00000000..0f3e59dc --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/gui/markdown/TokenWidget.java @@ -0,0 +1,37 @@ +package com.feed_the_beast.ftblib.lib.gui.markdown; + +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.Theme; +import net.minecraft.client.renderer.GlStateManager; + +public class TokenWidget extends BaseMarkdownElementWidget { + protected String text; + protected int textFlags = 0; + + public TokenWidget(Panel panel, String text) { + super(panel); + this.text = text; + + Theme theme = panel.getGui().getTheme(); + this.setWidth(theme.getStringWidth(text)); + this.setHeight(theme.getFontHeight()); + } + + public TokenWidget addFlags(int flags) { + this.textFlags |= flags; + return this; + } + + public String getText() { + return text; + } + + @Override + public void draw(Theme theme, int x, int y, int w, int h) { + GlStateManager.pushMatrix(); + GlStateManager.translate(x + posX, y + posY, 0); + theme.drawString(text, 0, 0, textFlags); + + GlStateManager.popMatrix(); + } +} \ No newline at end of file diff --git a/src/main/java/com/feed_the_beast/ftblib/lib/util/markdown/MarkdownBuilder.java b/src/main/java/com/feed_the_beast/ftblib/lib/util/markdown/MarkdownBuilder.java new file mode 100644 index 00000000..71b1fb1b --- /dev/null +++ b/src/main/java/com/feed_the_beast/ftblib/lib/util/markdown/MarkdownBuilder.java @@ -0,0 +1,74 @@ +package com.feed_the_beast.ftblib.lib.util.markdown; + +import com.feed_the_beast.ftblib.lib.gui.Panel; +import com.feed_the_beast.ftblib.lib.gui.markdown.MarkdownPanel; +/** + * Builder for creating and customizing MarkdownPanel + */ +public class MarkdownBuilder { + private final Panel parentPanel; + private final String id; + private int maxWidth = 0; + private int imageMargin = 5; + private String markdownText; + + /** + * Creates a new builder for MarkdownPanel + * + * @param parentPanel parent panel + * @param id panel id + */ + public MarkdownBuilder(Panel parentPanel, String id) { + this.parentPanel = parentPanel; + this.id = id; + } + + /** + * Sets the maximum width. + * + * @param width maximum width + * @return builder for call chain + */ + public MarkdownBuilder maxWidth(int width) { + this.maxWidth = width; + return this; + } + /** + * Sets padding for images + * + * @param margin padding + * @return builder for call chain + */ + public MarkdownBuilder imageMargin(int margin) { + this.imageMargin = margin; + return this; + } + + /** + * Set Markdown text + * + * @param text Markdown text + * @return builder for call chain + */ + public MarkdownBuilder markdown(String text) { + this.markdownText = text; + return this; + } + + /** + * Creates and configures MarkdownPanel + * + * @return configured MarkdownPanel + */ + public MarkdownPanel build() { + MarkdownPanel panel = new MarkdownPanel(parentPanel, id) + .setMaxWidth(maxWidth) + .setImageMargin(imageMargin); + + if (markdownText != null) { + panel.setText(markdownText); + } + + return panel; + } +} \ No newline at end of file