-
Notifications
You must be signed in to change notification settings - Fork 2
Home
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.
Adding ImproperUI to your project is fairly easy. This can be done in 3 simple steps:
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")
}
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.
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 implementsModInitializer
) -
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"
);
}
}
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, 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();
}
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. |
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 | ✅ | ✅ | ✅ |
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 {\
}
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.