Table of Contents
Main ideas
setup()
draw()
at a unique path, which will
Quality of life features
Find LazyGui in the Contribution Manager under the Libraries tab and click Install
See example sketches: File -> Examples... -> Contributed Libraries
Leaf through the offline javadocs: Help -> Libraries Reference -> LazyGui
Get the latest jar file from releases and then import it into your project using your IDE as a standard java library just like you imported Processing.
import com.krab.lazy.*;
LazyGui gui;
void setup(){
size(800,800,P2D);
gui = new LazyGui(this);
}
void draw(){
background(gui.colorPicker("background").hex);
}
The gui displays itself at the end of draw()
and by default it shows a root folder that can't be closed with two built in folders
// simplest getter for an infinite slider
float x = gui.slider("x");
// alternative getters that specify defaults and constraints
gui.slider("x", defaultFloat);
gui.slider("x", defaultFloat, minimumFloat, maximumFloat);
// setters
gui.sliderAdd("x", floatToAdd);
gui.sliderSet("x", floatToSet);
sliderInt()
alternative that uses and returns int
// simplest getter
PVector pos = gui.plotXY("position");
// alternative getters that specify defaults
gui.plotXY("position", defaultFloatXYZ);
gui.plotXY("position", defaultFloatX, defaultFloatY);
gui.plotXY("position", defaultPVector);
//setters
gui.plotSet("position", valueFloat);
gui.plotSet("position", valueFloatX, valueFloatY);
gui.plotSet("position", valuePVector);
plotXYZ()
variant with an extra Z slider (not connected to the grid)// simplest getter
PickerColor myColor = gui.colorPicker("background");
// use it with .hex in place of color
background(myColor.hex);
// alternative getters that specify the default color
gui.colorPicker("background", color(36));
gui.colorPicker("background", grayNorm); // 'norm' meaning float in the range [0, 1]
gui.colorPicker("background", hueNorm, saturationNorm, brightnessNorm);
gui.colorPicker("background", hueNorm, saturationNorm, brightnessNorm, alphaNorm);
// setters
gui.colorPickerSet("background", color(36));
gui.colorPickerHueAdd("background", hueToAdd);
// simple getter
PGraphics bgGradient = gui.gradient("background gradient");
image(bgGradient, 0, 0);
// alternative getter that specifies the default colors
gui.gradient("name", new int[]{color(255,0,150), color(0,150,0), color(0,100,150)});
// alternative getter which allows you to specify default colors and positions
// it uses varargs so you can use two or more gui.colorPoint() parameters
gui.gradient("name",
gui.colorPoint(color(255, 0, 0), 0f),
gui.colorPoint(color(0, 255, 0), 0.5f),
gui.colorPoint(color(0, 0, 255), 1f)
);
// special getter for a color inside the gradient at a position in range [0, 1]
// faster than texture.get(x, y) thanks to a color look up table
PickerColor myColor = gui.gradientColorAt("name", positionNorm);
// getter that is only true once after being clicked and then switches to false
boolean clear = gui.button("clear");
if(clear){
background(0.1);
println("background cleared");
}
// simple getter
boolean isToggledOn = gui.toggle("spam every frame")
if(isToggledOn){
println("I'm trapped in a string factory");
}
// alternative getter that specifies a default
gui.toggle("spam every frame", booleanDefault)
// setter
gui.toggleSet("spam every frame", booleanValue)
// simple getter
String userInput = gui.text("text header");
// getter that specifies a default content
gui.text("text header", "this default text can be edited");
gui.text("", "this will rename its parent folder");
// one time setter that also blocks any interaction when called every frame
gui.textSet("text header", "content")
Mouse Hotkey | Action under mouse |
---|---|
Enter | insert new line |
Delete | delete entire string |
Backspace | delete last character |
// simplest getter
String mode = gui.radio("mode", new String[]{"square", "circle"});
if (mode.equals("square")) {
rect(175, 175, 50, 50);
} else {
ellipse(200, 200, 50, 50);
}
// getter that specifies a default
gui.radio("mode", stringArray, defaultOption);
// setter that changes the currently selected option
gui.radioSet("mode", "square");
// setter that specifies new options for an existing radio
gui.radioSetOptions("mode", new String[]{"square", "circle", "triangle"});
radioSetOptions()
String[]
array of options you can also use List<String>
or ArrayList<String>
Global hotkey | Action |
---|---|
H | Hide GUI / Show GUI |
D | Close windows |
I | Save screenshot |
CTRL + Z | Undo |
CTRL + Y | Redo |
CTRL + S | New save |
Mouse hotkey | Action on element under mouse |
---|---|
Right click | Close window |
R | Reset value to default |
CTRL + C | Copy value or folder |
CTRL + V | Paste to value or folder |
Interacting with your sketch using the mouse can be very useful, but the GUI can get in the way, usually you don't want the sketch to react when you're dragging a slider in the GUI.
Unfortunately the GUI has no way to block the sketch from receiving the mouse event, but it can tell you whether the mouse has interacted with the GUI thanks to the isMouseOutsideGui()
method.
void mousePressed(){
if(gui.isMouseOutsideGui()){
// do something at the mouse
}
}
see: isMouseOutsideGui(), isMouseOverGui(), MouseDrawing
The GUI draws itself at the end of draw() by default, but you can override this by calling gui.draw()
before that happens. The GUI will never draw itself more than once per frame, so the automatic execution is skipped when this is called manually.
This can be useful in cases like including the GUI in a recording or using the PeasyCam library where you probably want to display the GUI between cam.beginHUD()
and cam.endHUD()
to separate the GUI overlay from the camera controlled 3D scene.
see: draw(), PeasyCamExample
The GUI can save its current values to disk in a json file. It can also load these values to overwrite the current GUI state.
You can control this from the saves
folder under the root window of the GUI. Any new, renamed and deleted save files will be detected by this window at runtime.
saves/create new save
or CTRL + S
saves/autosave rules
folder
saves
window
The path is the first string parameter to every control element function, and it must be unique.
It exists only in memory to inform the GUI - it's not a directory structure in any file storage.
The forward slash /
is a reserved character used to make folders, but it can be escaped with \\
like this: \\/
which won't separate folders.
float frq = gui.slider("wave/frequency");
float amp = gui.slider("wave/amplitude");
boolean state = gui.toggle("off\\/on");
Repeating the whole path in every control element call can get tiresome, especially with multiple nested folders.
Which is why there's a helpful path stack that you can interact with using pushFolder()
and popFolder()
.
Just like using pushMatrix()
and popMatrix()
in Processing, you can change your "current directory"
by pushing a new folder name to a stack with gui.pushFolder("folder name")
and have every control element called after that be placed into that folder automatically
as if the contents of the whole current stack got prefixed to every path parameter you use while calling the GUI.
popFolder()
doesn't have a parameter - it just returns by one levelYou can nest a pushFolder()
inside another pushFolder()
- your path stack can be many levels deep.
Just remember to call popFolder()
the same number of times - the stack does get cleared after the end of draw() before the GUI starts drawing itself, but it's better not to rely on that.
gui.pushFolder("wave");
float frq = gui.slider("frequency");
float amp = gui.slider("amplitude");
gui.popFolder();
println(gui.getFolder());
see javadocs: pushFolder(), [popFolder()](https://krabcode.github.io/LazyGui/com/krab/lazy/LazyGui.html#popFolder()), [getFolder()](https://krabcode.github.io/LazyGui/com/krab/lazy/LazyGui.html#getFolder())
You can hide folders and single elements from code, while still receiving their values in code - the only change is visual. This is helpful when you have a loop for folders whose paths differ by the index, and you create too many of these folders and then want to hide some of them. You can also use this to hide the default 'options' or 'saves' folders.
gui.hide("myPath") // hide anything at this path (the prefix stack applies here like everywhere else)
gui.show("myPath") // reveal anything previously hidden at this path
gui.hideCurrentFolder() // hide the folder at the current path prefix stack
gui.showCurrentFolder() // show the folder at the current path prefix stack if it has been previously hidden
You can check whether a value has changed last frame with gui.hasChanged("myPath")
and gui.hasChanged()
.
This can be useful when you don't want to do an expensive operation every frame but only when its controlling parameters change.
This works with single control elements, but it also works recursively through any child elements, so you can call it on a folder, and it will return true if any value nested under it has changed.
The result after a change is only true for one frame after a change and then gets reset to false for the next frame. These functions respect the current path stack. They do not initialize any new control elements or folders. Calling the function does not flip the value to false by itself.
See the UtilityMethods example which uses it to load a PFont whenever the user changes the font name or size.
Runtime changes of what a folder row looks like in its parent window. This helps with organizing folders, especially with folder paths that differ only by the index inside a loop.
A folder will display a little 'on' light in its icon when at least one toggle inside the folder is set to true and its name matches one of the following:
A folder will display a name editable at runtime when there is a text control whose name matches one of the following:
You can initialize your gui with an extra settings object to set various global defaults and affect startup and exit behavior. Loading a save overwrites these, but you can also disable loading on startup here.
Here's how to use it in a builder chain where the ordering does not matter (except for conflicting instructions):
gui = new LazyGui(this, new LazyGuiSettings()
// set as false to not load anything on startup, true by default
.setLoadLatestSaveOnStartup(false)
// expects filenames like "1" or "auto.json", overrides 'load latest'
.setLoadSpecificSaveOnStartup("1")
// controls whether to autosave, true by default
.setAutosaveOnExit(false)
// windows will never 'restore' when loading a save (allowed once at startup by default)
.setWindowRestoreNever()
);
When you load a save, the GUI will try to restore the window state to what it was when the save was made. This includes the position, size, and open/closed state of each window. There are three available modes, selected using the Constructor settings.
setWindowRestoreNever()
setWindowRestoreOnlyOnStartup()
<= default, a balance of startup convenience and control at runtimesetWindowRestoreAlways()
This GUI includes the (slightly out of scope) ShaderReloader class that watches your shader files as you edit them and re-compiles them when changes are made. If an error occurs during compilation, it keeps using the last compiled state and prints out the error to console.
Example using a fragment shader:
String shaderPath = "template.glsl";
PShader shader = ShaderReloader.getShader(shaderPath);
shader.set("time", (float) 0.001 * millis());
ShaderReloader.filter(shaderPath);
For shader compilation to work, ShaderReloader needs a reference to a PApplet, so in setup()
:
new LazyGui(this)
as seen in the minimal code exampleShaderReloader.setApplet(this)
in case you don't need the GUI in your sketchThis GUI also includes the Input utility that makes it easier to see whether any number of keys are currently pressed on the keyboard. Processing only shows you one key at a time while this utility keeps track of past events and can tell you whether any char or keyCode was just pressed, is currently held down or if it was just released.
Example detecting CTRL + SPACE with Input class static methods:
boolean isControlDown = Input.getCode(CONTROL).down;
boolean spaceWasJustPressed = Input.getChar(' ').pressed;
if(isControlDown && spaceWasJustPressed){
println("ctrl + space pressed");
}
see: Input javadocs
LazyGui runs on all of these:
develop
branch (or make your own branch based on develop
), test your changes and submit a pull requestlink gradle project
which should call gradle build
and set all the source folders correctlycom/krab/lazy/examples_intellij
directory contains runnable examples which you can edit and run to test your changes to the GUI