Multiline Controller
This example covers the basics of custom controller creation. It shows you how to make a controller that targets a primitive value, like a string.
Imagine you need to edit a string that has line breaks. That's not really possible with lil-gui's <input>
based string controller, so we'll make a custom controller that uses a <textarea>
instead.
Our controller adds a new method called gui.addMultiline( obj, prop )
, which assumes that obj.prop
is a string. Before looking at the source for the controller itself, take a look at the code that puts it to use.
import GUI from 'lil-gui';
import './MultilineController.js';
const gui = new GUI();
const obj = {
text: 'one\ntwo\nthree'
};
gui.addMultiline( obj, 'text', 6 );
Type in the controller and see how the debug reacts.
To make a custom controller, we start by extending lil-gui's CustomController
class. The following is the source of MultilineController
. It highlights the methods and properties used to integrate with lil-gui, most of which are prefixed with $
.
import CustomController from 'lil-gui/extras/CustomController.js';
export default class MultilineController extends CustomController {
$constructor( rows = 3 ) {
this.textarea = document.createElement( 'textarea' );
this.textarea.setAttribute( 'rows', rows );
this.$widget.appendChild( this.textarea );
this.textarea.addEventListener( 'input', () => {
this.$value = this.textarea.value;
} );
this.textarea.addEventListener( 'blur', () => {
this.$onFinishChange();
} );
}
$updateDisplay() {
this.textarea.value = this.$value;
}
}
MultilineController.$id = 'Multiline';
MultilineController.$style =
`.lil-gui .controller.Multiline textarea {
/* Apply built-in vars. */
background: var(--widget-color);
color: var(--string-color);
font-family: var(--font-family);
font-size: var(--input-font-size);
padding: var(--spacing);
min-height: var(--widget-height);
border-radius: var(--widget-border-radius);
/* Override browser defaults. */
width: 100%;
border: 0;
outline: none;
resize: vertical;
}
.lil-gui .controller.Multiline textarea:hover {
background: var(--hover-color);
}
.lil-gui .controller.Multiline textarea:focus {
background: var(--focus-color);
}`;
CustomController.register( MultilineController );
$constructor and $updateDisplay
Our controller never defines a literal constructor—only a method named $constructor
. This allows the CustomController
class to execute code before and after our initialization logic.
You'll see a similar pattern with methods like $updateDisplay
. The Controller
class already has a method by that name, but without the prefix.
CustomController
provides these $
methods to spare you from boilerplate code. You should always implement the $
version as opposed to implementing the un-prefixed method directly.
$id
The $id
property serves two purposes: it defines a "namespace" for our CSS selectors, and it's used to name the convenience method added to GUI's prototype: addMultiline
. Note that this method is just syntactic sugar:
gui.addMultiline( obj, 'prop', 6 );
new MultilineController( gui, obj, 'prop', 6 );
$style
The CSS in our $style
string is added to the page in the same way that lil-gui adds its base stylesheet: by injecting a <style>
tag in the document's head, before any other style tags or imported CSS files.
We don't want our stylesheet to change every <textarea>
that might appear on the page—just the ones inside our custom controller. To prevent this, prefix your selectors with .lil-gui .controller.YOUR_ID
.
lil-gui uses CSS variables to allow user theming. Custom controllers should hook into these variables wherever possible. For a complete guide on relevant CSS variables and DOM structures, see Styling Custom Controllers.
Summary
To make a controller that targets a primitive:
- Import and extend
CustomController
.
- Implement
$constructor
and $updateDisplay
.
- Provide a name via a static string called
$id
(UpperCamel).
- Provide a stylesheet via a static string called
$style
.
- Register the controller class with
CustomController.register
.
The next example creates a controller that targets an object: XY Controller.