lil-gui › Examples
:
Multiline Controller

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.

This page:
import GUI from 'lil-gui';
import './MultilineController.js';

const gui = new GUI();

const obj = {
	text: 'one\ntwo\nthree'
};

// The third parameter defines the height of the textarea in rows.
gui.addMultiline( obj, 'text', 6 );

Type in the controller and see how the debug reacts.

Controller debug:
value: 
onChange
onFinishChange

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 $.

./MultilineController.js
// CustomController isn't part of the core library, so the import looks different.
import CustomController from 'lil-gui/extras/CustomController.js';

export default class MultilineController extends CustomController {

	// $constructor is called on creation.
	// It receives all the parameters after gui.addMultiline( object, property, ... )
	$constructor( rows = 3 ) {

		this.textarea = document.createElement( 'textarea' );
		this.textarea.setAttribute( 'rows', rows );

		// UI elements need to be added to $widget to appear in the controller.
		this.$widget.appendChild( this.textarea );

		this.textarea.addEventListener( 'input', () => {

			// Use this.$value to update the controller's value. Assignments to this
			// property will automatically update the display and fire onChange events.
			this.$value = this.textarea.value;

		} );

		this.textarea.addEventListener( 'blur', () => {

			// Call $onFinishChange whenever your controls lose focus.
			this.$onFinishChange();

		} );

	}

	// $updateDisplay should update the controller to reflect the current value.
	$updateDisplay() {
		this.textarea.value = this.$value;
	}

}

// Provide a name for your controller by defining an $id string (UpperCamelCased).
// Custom controllers give the GUI a method called "add" + $id.
// That's where addMultiline comes from. It's also used to scope your controller's styles.

MultilineController.$id = 'Multiline';

// Provide a stylesheet for your controller by defining a $style string.
// Prefix your selectors with ".lil-gui .controller.$id" to keep them in scope.
// See Styling Custom Controllers for a guide on custom controller CSS.

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);
}`;

// Finally, register the controller with lil-gui. This injects our stylesheet
// and adds the "addMultiline" method to GUI's prototype.

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:

// Both statements are equivalent
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:


The next example creates a controller that targets an object: XY Controller.