Skip to content
ImproperIssues edited this page May 20, 2024 · 3 revisions

Introduction to ImproperUI

ImproperUI is a custom scripting language heavily inspired by Cascading Style Sheets (CSS). It aims to boost productivity in creating Minecraft mods by enabling mod creators to design custom in-game GUIs without needing any knowledge of Minecraft's rendering processes.

Traditionally, developing a Minecraft GUI involves extending the original Minecraft Screen.java class and handling mouse or keyboard events manually. Developers must also construct a hierarchy of elements as children to the screen, with each child potentially having its own children, leading to a complex tree structure. Each child requires its own event system and display setup, making the process time-consuming, especially for simple GUIs like those in auto-clicker mods.

ImproperUI simplifies this process significantly. It allows you to design a custom GUI using a language similar to CSS and trigger Java methods using Java reflection, enabling seamless communication between the script and your program. ImproperUI represents a forward-looking solution for Minecraft mods seeking to incorporate custom UIs.

Implementation

Adding ImproperUI to your project is fairly easy. This can be done in 3 simple steps:

Step 1

Download the JAR file and add it to your project files. By convention, the JAR file should be inside of your <root project folder>/libs folder. Once you have added the downloaded JAR to your project, declare the dependency in your build.gradle then refresh Gradle dependencies.

dependencies {

    compileOnly files("libs/ImproperUI-theVersionYouWant.jar")

}

Step 2

Add the downloaded JAR file to your mods folder. This mod has to be used as a dependency mod. Think of this as another fabric-api JAR file.

Step 3

Initialize the API. To do this, you call the init() function on ImproperUIAPI then provide the following:

  • The modId of your mod

  • The main class of your mod that (the one that implements ModInitializer)

  • A list of script paths that are located in src/main/resources/ of your mod

  • By convention, you should create your scripts inside of src/main/resources/assets/YOURMODID/improperui/ folder

DO NOTE THAT WHEN YOU ACTUALY TRY TO PARSE OR RUN THE SCRIPTS, YOU REFERENCE THE SCRIPT FILE'S NAME NOT THE PATH DECLARED HERE!

public class ImproperUI implements ModInitializer {

    @Override

    public void onInitialize() {

        ImproperUIAPI.init("improperui", ImproperUI.class,

                "assets/improperui/improperui/what.ui",

                "assets/improperui/improperui/screen.ui"

        );

    }

}
public class YourModInitializer implements ModInitializer {

    @Override

    public void onInitialize() {

        ImproperUIAPI.init("yourModId", YourModInitializer.class,

                "assets/yourModId/improperui/yourscreen1.ui",

                "assets/yourModId/improperui/yourscreen2.ui"

        );

    }

}

Events & Callbacks

To listen for events declared in your script, create a new class that implements CallbackListener. Each callback method has to be annotated by @CallbackHandler and have at least one parameter that extends Event. The name of your method should match the name of the declared event in your script.

public class CustomCallbacks implements CallbackListener {

    

    @CallbackHandler

    public void sendHelloWorld(MouseEvent e) {

        if (e.input.isDown())

            ChatUtils.sendMessage("Hello World");

    }

}

In your script, your event should look like this:


element {

    on-click: sendHelloWorld

}

Screens/Panels

Screens, or Panels, however you want to call them, are objects that host your children elements. They are also responsible for handling and passing down events such as element drag, click or even keyboard actions. 

You open a panel by passing in a modId and a script file’s name for the parseFileAndRun() function on ImproperUIAPI.

public void openScreen() {

    ImproperUIAPI.parseAndRunFile("yourModId", "testing.ui", new CustomCallback() /\* and more... \*/);

}

Alternatively, you can also create a panel manually, then registering the callbacks and adding the children elements yourself.

public void openScreen() {

    ImproperUIPanel panel = new ImproperUIPanel();

    panel.registerCallback(new CustomCallback());

    // parse script and add children elements here

    // panel.addChild()

    panel.open();

}

Helper Methods

These methods are intialized:

Method  Description
ImproperUIPanel.collect() A list of all elements and widgets, even their children
ImproperUIPanel.collectOrdered() A sorted list based on z-index, of all elements and widgets including their children
ImproperUIPanel.collectById() A list of elements with specified ID
ImproperUIPanel.collectByClassAttribute() A list of elements with specified class attribute
ImproperUIPanel.collectFirstById() First element with specified ID
ImproperUIPanel.collectFirstByClassAttribute() First element with specified class attribute
ImproperUIPanel.open() Opens the panel up for the player/client.

These methods are static:

Method Description
ImproperUIAPI.parse() Parses a script then returns all parsed result elements
ImproperUIAPI.parseAndRunFile() Parses a registered script file NAME (NOT PATH) from init() and opens the screen with the elements 
ImproperUIAPI.parseAndRunScript() Parses a registered script from init() and opens the screen with the elements
ImproperUIAPI.reload() Reloads the API
ImproperUIAPI.init() Re-init the API with possibly a different Mod ID
ImproperUIAPI.collectContext() Returns a list of all registered initialization contexes. Mod developers can use this to see which mods are using ImproperUI.
ImproperUIAPI.getContext() Gets a specific initialization contex given the modId passed in the parameter.

Syntax

Tags

Tags in ImproperUI are just like the tags seen in HTML. Each tag has a different preset of properties therefore serves a different functionality.

Element Tag Dynamic Hover, Select, Focus Children Support Config Support Aliases Specific Properties: type
element e, div
checkbox active:boolean
radio active:boolean
button
link a href:string
slider min:double max:doublevalue:double range:double,double decimal-places:integer
input textbox pattern:quote placeholder:quote
textfield textarea
label textlabel
header1 h1
header2 h2
header3 h3
header4 h4
header5 h5
header6 h6
position

IDs & Attribute Classes

Element IDs are declared with a prefix #, and attribute classes are declared with a prefix -. Each element can have only one ID, but can have multiple classes. These classes gets stored in the element’s classlist.


element #element-id -class1 -class2 -class3 {\
}

Config Keys

A config key consists of three parts: modId, confileFile, propertyName. They are used for saving configuration values for, let's say, your sliders and checkboxes.

To use a config key in your script, simply type out all three parts WITH NO SPACES AND SEPARATED BY A COLON (:). Then use that string as an attribute class for the element:


slider #someId -yourModId:config.properties:testing-slider-value -someAnotherAttributeClass {

    

}

This creates a slider that sets and saves values do the config.