-
Notifications
You must be signed in to change notification settings - Fork 1
Introduction
This update brings significant changes:
- XML Support for HTML
- YAML Support for CSS
- Various other improvements, fixes and features
Why are XML and YAML so important?
Because they offer an easier and faster way to build your GUI without the need for loops or complex math.
BUI implements a "DOM" (Document Object Model) and styling sheets.
This means you can use the following code:
BUI Main Menu
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="GUI-LIBRARY-HTML-SCHEMA">
<link src="/assets/guilib/uis/defaults/defaults.yaml"/>
<link src="/assets/guilib/uis/UIbui/styles.yaml"/>
<body>
<div id="mainContainer">
<label id="mainTitle" maxTextLength="1000">Welcome to BUI</label>
<button id="backButton">
<label>
x
</label>
</button>
<label id="copyright">
@Garkatron
</label>
<panel id="centerContainer">
<expandableButton id="detectedProjectsButton">
<label>
Detected Projects
</label>
</expandableButton>
</panel>
</div>
</body>
</root>SharedProperties:
textShadow: false
# Selectors
Select:
- at: expandablebutton
panelLengthX: 80
childrenPlacement: CENTER
panelLengthY: 10
border: -1px 000000
- at: .bui.options.pathButton
width: 100%
childrenPlacement: CENTER
backgroundColor: "FF0000"
- at: $mainContainer
width: 100%
height: 100%
backgroundColor: "585250"
- at: $centerContainer
#tileSize: 4px
#backgroundImage: /assets/guilib/textures/gui/themes/default/SmallTinyPanel.png
panelLengthY: 50
panelLengthX: 80
selfPlacement: CENTER
childrenPlacement: CENTER
- at: $mainTitle
selfPlacement: TOP
textColor: "000000"
MainGuiMenu.class
public class MainPage extends Page {
public MainPage(Router router, String... text) {
super(GuiLib.class, router);
xmlPath = "/assets/guilib/uis/UIbui/index.xml";
// ! DON'T DELETE IT
reloadXml();
reloadStyles();
// ! Work here
IButton b = (IButton) getDocument().getNodeById("detectedProjectsButton");
b.setOnReleaseAction(
(v) -> {
router.navigateTo("detectedProjectsPage");
}
);
}
}//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package net.minecraft.client.gui;
import java.awt.Desktop;
import java.awt.Desktop.Action;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.options.GuiOptions;
import net.minecraft.client.gui.options.data.OptionsPages;
import net.minecraft.client.render.RenderBlocks;
import net.minecraft.client.render.block.model.BlockModel;
import net.minecraft.client.render.block.model.BlockModelDispatcher;
import net.minecraft.client.render.stitcher.TextureRegistry;
import net.minecraft.client.render.tessellator.Tessellator;
import net.minecraft.core.block.Block;
import net.minecraft.core.lang.I18n;
import net.minecraft.core.sound.SoundCategory;
import net.minecraft.core.util.helper.MathHelper;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
public class GuiMainMenu extends GuiScreen {
private static final Block[] BLOCKS_TO_DRAW;
private static String[] logoBlockLayers;
private static final int[] KEY_CHAIN;
private static final Random rand;
private String splashText = "missingno";
private GuiButton multiplayerButton;
private LogoBlock[][] logoBlocks;
private int logoClickCounter = 0;
private int indexToDraw = 0;
private int codeProgress = 0;
private boolean showBuildTime = false;
public GuiMainMenu() {
GuiIngameMenu.photoModeDisabled = false;
InputStream stream = null;
try {
stream = GuiMainMenu.class.getResourceAsStream("/title/splashes.txt");
List<String> allSplashes = new ArrayList();
BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
while(true) {
String line = br.readLine();
if (line == null) {
this.splashText = (String)allSplashes.get(rand.nextInt(allSplashes.size()));
break;
}
line = line.trim();
if (!line.isEmpty()) {
allSplashes.add(line);
}
}
} catch (Exception var13) {
} finally {
try {
stream.close();
} catch (Exception var12) {
}
}
}
public void keyTyped(char c, int i, int mouseX, int mouseY) {
if (i == KEY_CHAIN[this.codeProgress]) {
++this.codeProgress;
if (this.codeProgress == KEY_CHAIN.length) {
if (this.mc.ingameGUI instanceof GuiIngameDoom) {
this.mc.ingameGUI = new GuiIngame(this.mc);
} else {
this.mc.ingameGUI = new GuiIngameDoom(this.mc);
}
this.mc.sndManager.playSound("mob.pig", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F);
this.codeProgress = 0;
}
} else {
this.codeProgress = 0;
}
}
public void init() {
I18n i18n = I18n.getInstance();
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
if (calendar.get(2) + 1 == 1 && calendar.get(5) == 19) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.jonkadelic_birthday");
} else if (calendar.get(2) + 1 == 4 && calendar.get(5) == 29) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.maggandgeez_birthday");
} else if (calendar.get(2) + 1 == 5 && calendar.get(5) == 17) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.minecraft_birthday");
} else if (calendar.get(2) + 1 == 7 && calendar.get(5) == 20) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.bta_birthday");
} else if (calendar.get(2) + 1 == 12 && calendar.get(5) == 24) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.xmas");
} else if (calendar.get(2) + 1 == 1 && calendar.get(5) == 1) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.new_year");
} else if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31) {
this.splashText = i18n.translateKey("gui.main_menu.label.splash.halloween");
}
int i = this.height / 4 + 48;
this.controlList.add(new GuiButton(1, this.width / 2 - 100, i, i18n.translateKey("gui.main_menu.button.singleplayer")));
this.controlList.add(this.multiplayerButton = new GuiButton(2, this.width / 2 - 100, i + 24, i18n.translateKey("gui.main_menu.button.multiplayer")));
this.controlList.add(new GuiButton(3, this.width / 2 - 100, i + 48, i18n.translateKey("gui.main_menu.button.texture_packs")));
if (this.mc.hideQuitButton) {
this.controlList.add(new GuiButton(0, this.width / 2 - 100, i + 72, i18n.translateKey("gui.main_menu.button.options")));
} else {
this.controlList.add(new GuiButton(0, this.width / 2 - 100, i + 72 + 12, 98, 20, i18n.translateKey("gui.main_menu.button.options")));
this.controlList.add(new GuiButton(4, this.width / 2 + 2, i + 72 + 12, 98, 20, i18n.translateKey("gui.main_menu.button.quit_game")));
}
int d = 1;
this.controlList.add(new GuiClickableLabel(5, d, this.height - 36 - d, i18n.translateKey("gui.main_menu.button.website"), 5941560));
this.controlList.add(new GuiClickableLabel(6, d, this.height - 24 - d, i18n.translateKey("gui.main_menu.button.discord"), 5793266));
this.controlList.add(new GuiClickableLabel(7, d, this.height - 12 - d, i18n.translateKey("gui.main_menu.button.youtube"), 16711680));
this.controlList.add(new GuiTexturedButton(8, "/assets/minecraft/textures/gui/gui.png", this.width / 2 + 104, i + 72 + 12, 0, 86, 20, 20));
GuiButton debugButton = new GuiButton(9, this.width - 8, this.height - 8, 8, 8, "");
debugButton.visible = false;
this.controlList.add(debugButton);
if (this.mc.session == null) {
this.multiplayerButton.enabled = false;
}
}
protected void buttonPressed(GuiButton button) {
if (button.id == 0) {
this.mc.displayGuiScreen(new GuiOptions(this));
}
if (button.id == 1) {
this.mc.displayGuiScreen(new GuiSelectWorld(this));
}
if (button.id == 2) {
this.mc.displayGuiScreen(new GuiSelectServer(this));
}
if (button.id == 3) {
this.mc.displayGuiScreen(new GuiOptions(this, OptionsPages.TEXTURE_PACKS));
}
if (button.id == 4) {
this.mc.shutdown();
}
if (button.id == 8) {
this.mc.displayGuiScreen(new GuiOptions(this, OptionsPages.LANGUAGE));
}
if (button.id == 9) {
this.mc.displayGuiScreen(new GuiOptions(this, OptionsPages.DEBUG));
}
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Action.BROWSE)) {
Exception e;
if (button.id == 5) {
try {
desktop.browse(URI.create("https://betterthanadventure.net"));
} catch (Exception var6) {
e = var6;
e.printStackTrace();
}
}
if (button.id == 6) {
try {
desktop.browse(URI.create("https://betterthanadventure.net/discord"));
} catch (Exception var5) {
e = var5;
e.printStackTrace();
}
}
if (button.id == 7) {
try {
desktop.browse(URI.create("https://www.youtube.com/channel/UC_GT4t8Io87-UTzaGNnsuMg"));
} catch (Exception var4) {
e = var4;
e.printStackTrace();
}
}
}
}
public void drawBackground() {
if (this.mc.mainMenuBackground.isEnabled() && !(Boolean)this.mc.gameSettings.alphaMenu.value) {
this.mc.mainMenuBackground.draw();
} else {
super.drawBackground();
}
}
public void tick() {
if (this.logoBlocks != null) {
for(int var1 = 0; var1 < this.logoBlocks.length; ++var1) {
for(int var2 = 0; var2 < this.logoBlocks[var1].length; ++var2) {
this.logoBlocks[var1][var2].updateLogoEffects();
}
}
}
}
public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
super.mouseClicked(mouseX, mouseY, mouseButton);
int logoWidth = 274;
int logoX = this.width / 2 - logoWidth / 2;
int logoHeight = 30;
int p = 8;
if (mouseX >= logoX + p && mouseX <= logoX + 270 - p && mouseY >= logoHeight + p && mouseY <= logoHeight + 48 - p) {
if (this.logoClickCounter >= 0) {
this.indexToDraw = rand.nextInt(BLOCKS_TO_DRAW.length);
this.mc.gameSettings.alphaMenu.toggle();
if ((Boolean)this.mc.gameSettings.alphaMenu.value) {
this.logoBlocks = null;
}
this.logoClickCounter = 0;
} else {
++this.logoClickCounter;
}
}
if (mouseY >= 2 && mouseY <= 12 && mouseX >= 2 && mouseX <= 40) {
this.showBuildTime = !this.showBuildTime;
}
}
public void drawScreen(int mouseX, int mouseY, float partialTick) {
I18n i18n = I18n.getInstance();
this.drawDefaultBackground();
char c = 274;
int k = this.width / 2 - c / 2;
byte byte0 = 30;
if (!(Boolean)this.mc.gameSettings.alphaMenu.value) {
GL11.glBindTexture(3553, this.mc.renderEngine.getTexture("/title/mclogo.png"));
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.drawTexturedModalRect(k + 0, byte0 + 0, 0, 0, 155, 64);
this.drawTexturedModalRect(k + 155, byte0 + 0, 0, 64, 155, 64);
} else {
this.drawLogo(partialTick);
}
GL11.glPushMatrix();
GL11.glTranslatef((float)(this.width / 2 + 140), 80.0F, 0.0F);
GL11.glRotatef(-20.0F, 0.0F, 0.0F, 1.0F);
float f1 = 1.8F - MathHelper.abs(MathHelper.sin((float)(System.currentTimeMillis() % 1000L) / 1000.0F * 3.141593F * 2.0F) * 0.1F);
f1 = f1 * 100.0F / (float)(this.fontRenderer.getStringWidth(this.splashText) + 32);
GL11.glScalef(f1, f1, f1);
this.drawStringCentered(this.fontRenderer, this.splashText, 0, -8, 16776960);
GL11.glPopMatrix();
int versionTextColor = !(Boolean)this.mc.gameSettings.alphaMenu.value && this.mc.mainMenuBackground.isEnabled() ? 16777215 : 5263440;
if (this.showBuildTime) {
this.drawString(this.fontRenderer, "2024-08-04 15:34:59", 2, 2, versionTextColor);
} else {
this.drawString(this.fontRenderer, "Better than Adventure! " + Minecraft.VERSION, 2, 2, versionTextColor);
}
String disclaimer1 = i18n.translateKey("gui.main_menu.label.disclaimer.1");
this.drawString(this.fontRenderer, disclaimer1, this.width - this.fontRenderer.getStringWidth(disclaimer1) - 2, this.height - 20, 16777215);
String disclaimer2 = i18n.translateKey("gui.main_menu.label.disclaimer.2");
this.drawString(this.fontRenderer, disclaimer2, this.width - this.fontRenderer.getStringWidth(disclaimer2) - 2, this.height - 10, 16777215);
super.drawScreen(mouseX, mouseY, partialTick);
if (this.mc.downloadResourcesThread.downloading) {
double b = Math.sin((double)System.currentTimeMillis() / 1000.0 * 4.0);
b = b * 0.2 + 0.8;
int ib = (int)Math.round(b * 255.0);
int color = -16777216 + (ib << 16) + (ib << 8) + ib;
this.mc.fontRenderer.drawCenteredString(i18n.translateKeyAndFormat("gui.main_menu.label.downloading_resources", new Object[]{this.mc.downloadResourcesThread.downloadProgress, this.mc.downloadResourcesThread.toDownload}), this.width / 2, 16, color);
}
}
private void drawLogo(float var1) {
int var3;
int var4;
if (this.logoBlocks == null) {
this.logoBlocks = new LogoBlock[logoBlockLayers[0].length()][logoBlockLayers.length];
for(var4 = 0; var4 < this.logoBlocks.length; ++var4) {
for(var3 = 0; var3 < this.logoBlocks[var4].length; ++var3) {
this.logoBlocks[var4][var3] = new LogoBlock(var4, var3);
}
}
}
GL11.glMatrixMode(5889);
GL11.glPushMatrix();
GL11.glLoadIdentity();
var3 = 120 * this.mc.resolution.scale;
GLU.gluPerspective(70.0F, (float)this.mc.resolution.width / (float)var3, 0.05F, 100.0F);
GL11.glViewport(0, this.mc.resolution.height - var3, this.mc.resolution.width, var3);
GL11.glMatrixMode(5888);
GL11.glPushMatrix();
GL11.glLoadIdentity();
GL11.glDisable(2884);
GL11.glCullFace(1029);
GL11.glDepthMask(true);
GL11.glEnable(2929);
for(var4 = 0; var4 < 3; ++var4) {
GL11.glPushMatrix();
GL11.glTranslatef(0.4F, 0.6F, -12.0F);
if (var4 == 0) {
GL11.glClear(256);
GL11.glTranslatef(0.0F, -0.4F, 0.0F);
GL11.glScalef(0.98F, 1.0F, 1.0F);
GL11.glEnable(3042);
GL11.glBlendFunc(770, 771);
}
if (var4 == 1) {
GL11.glDisable(3042);
GL11.glClear(256);
}
if (var4 == 2) {
GL11.glEnable(3042);
GL11.glBlendFunc(768, 1);
}
GL11.glScalef(1.0F, -1.0F, 1.0F);
GL11.glRotatef(15.0F, 1.0F, 0.0F, 0.0F);
GL11.glScalef(0.89F, 1.0F, 0.4F);
GL11.glTranslatef((float)(-logoBlockLayers[0].length()) * 0.5F, (float)(-logoBlockLayers.length) * 0.5F, 0.0F);
TextureRegistry.blockAtlas.bindTexture();
if (var4 == 0) {
GL11.glBindTexture(3553, this.mc.renderEngine.getTexture("/title/black.png"));
}
RenderBlocks var5 = new RenderBlocks();
for(int var6 = 0; var6 < logoBlockLayers.length; ++var6) {
for(int var7 = 0; var7 < logoBlockLayers[var6].length(); ++var7) {
char var8 = logoBlockLayers[var6].charAt(var7);
if (var8 != ' ') {
GL11.glPushMatrix();
LogoBlock var9 = this.logoBlocks[var7][var6];
float var10 = (float)(var9.prevHeight + (var9.height - var9.prevHeight) * (double)var1);
float var11 = 1.0F;
float var12 = 1.0F;
if (var4 == 0) {
var11 = var10 * 0.04F + 1.0F;
var12 = 1.0F / var11;
var10 = 0.0F;
}
GL11.glTranslatef((float)var7, (float)var6, var10);
GL11.glScalef(var11, var11, var11);
var5.renderBlockAsItem(Tessellator.instance, (BlockModel)BlockModelDispatcher.getInstance().getDispatch(BLOCKS_TO_DRAW[this.indexToDraw]), BLOCKS_TO_DRAW[this.indexToDraw], var12);
GL11.glPopMatrix();
}
}
}
GL11.glPopMatrix();
}
GL11.glDisable(3042);
GL11.glMatrixMode(5889);
GL11.glPopMatrix();
GL11.glMatrixMode(5888);
GL11.glPopMatrix();
GL11.glViewport(0, 0, this.mc.resolution.width, this.mc.resolution.height);
GL11.glEnable(2884);
}
static Random getRandom() {
return rand;
}
public static void loadLogo() {
try {
InputStream inputStream = GuiMainMenu.class.getResourceAsStream("/title/retrologo.png");
BufferedImage image = ImageIO.read(inputStream);
inputStream.close();
logoBlockLayers = new String[image.getHeight()];
for(int i = 0; i < image.getHeight(); ++i) {
StringBuilder builder = new StringBuilder();
for(int j = 0; j < image.getWidth(); ++j) {
int rgb = image.getRGB(j, i);
if ((rgb >> 16 & 255) > 128) {
builder.append('*');
} else {
builder.append(' ');
}
}
logoBlockLayers[i] = builder.toString();
}
} catch (Exception var6) {
Exception e = var6;
e.printStackTrace();
}
}
static {
BLOCKS_TO_DRAW = new Block[]{Block.stone, Block.dirt, Block.planksOak, Block.cobbleStone, Block.cobbleStoneMossy, Block.basalt, Block.limestone, Block.granite, Block.brickClay, Block.ice, Block.wool, Block.blockSnow, Block.obsidian, Block.netherrack};
logoBlockLayers = null;
KEY_CHAIN = new int[]{23, 32, 31, 25, 23, 31, 25, 24, 25, 32, 28};
rand = new Random();
loadLogo();
}
class LogoBlock {
public double height;
public double prevHeight;
public double dropSpeed;
public LogoBlock(int var2, int var3) {
this.height = this.prevHeight = (double)(10 + var3) + GuiMainMenu.rand.nextDouble() * 32.0 + (double)var2;
}
public void updateLogoEffects() {
this.prevHeight = this.height;
if (this.height > 0.0) {
this.dropSpeed -= 0.6;
}
this.height += this.dropSpeed;
this.dropSpeed *= 0.9;
if (this.height < 0.0) {
this.height = 0.0;
this.dropSpeed = 0.0;
}
}
}
}To create your functional GUI, BUI provides many pre-built Elements for your GUI, so you don’t need to build everything from scratch. If you want to add your own custom element, you can extend the Node.java class or implement its interface INode.java.
BUI is modular, so you can use individual nodes to draw them in your classic GUI or, if necessary, use the advanced GUI classes for more complex GUIs in your mod. The simpler elements can be drawn by hand.
BUI also provides a GuiHelper class that helps with public static variables like mouseX and mouseY positions, as well as other functions to manage XML files.
XML files use a specific syntax. When I implemented it, I created a schema so that users can benefit from autocompletion with error checking in IntelliJ IDEA.
As shown above, you can use XML tags to design your GUI without writing any Java code.
Additionally, I worked on a Template System. You can load components from modules using their domain and use the "call" tag to "copy-paste" and keep your XML code clean. You can also load these templates from Java to use them like React components!
With YAML, you can style your GUI using simple style properties—without needing any Java code! It's similar to CSS.
With the current system, you can create your own custom styles for your mod.
- Resource Packs: Assets can overwrite XML and YAML files.
To master BUI quickly, here are the main classes you should be aware of:
-
INode.java
-
Root.java
-
Node.java
-
AdvancedContainer.java
-
AdvancedGuiContainer.java
-
AdvancedGuiScreen.java
-
Router.java