Widgets are UI elements that can be placed in custom windows. OpenRCT2 provides a variety of widget types for building interactive interfaces.
Available widget types:
"button" - Clickable button
"checkbox" - Checkbox with label
"colourpicker" - Colour selection widget
"custom" - Custom drawable widget
"dropdown" - Dropdown selection list
"groupbox" - Visual grouping box
"label" - Static text label
"listview" - Scrollable list with columns
"spinner" - Numeric spinner with +/- buttons
"textbox" - Text input field
"viewport" - Embedded viewport
All widgets share these common properties:
The window containing this widget.
The x position relative to the window.
The y position relative to the window.
The widget name (used with findWidget).
The tooltip text shown on hover.
Whether the widget is disabled.
Whether the widget is visible.
Clickable button with text or image.
{
type: 'button',
name: 'myButton',
x: 10,
y: 20,
width: 100,
height: 24,
text: 'Click Me',
border: true,
onClick: () => {
console.log('Button clicked!');
}
}
Whether the button has a 3D border. Default: true for text, false for images.
Whether the button appears pressed.
{
type: 'button',
x: 10,
y: 10,
width: 100,
height: 24,
text: 'Save',
onClick: () => {
// Save action
}
}
{
type: 'button',
x: 120,
y: 10,
width: 24,
height: 24,
image: 'guests',
tooltip: 'View Guests',
onClick: () => {
// Show guests
}
}
Checkbox with label.
{
type: 'checkbox',
name: 'pauseCheckbox',
x: 10,
y: 20,
width: 200,
height: 14,
text: 'Pause Game',
isChecked: false,
onChange: (checked) => {
context.paused = checked;
}
}
Whether the checkbox is checked.
Colour selection widget.
{
type: 'colourpicker',
name: 'colourPicker',
x: 10,
y: 20,
width: 24,
height: 24,
colour: 0,
onChange: (colour) => {
console.log(`Selected colour: ${colour}`);
}
}
Show Colour Picker Properties
The selected colour (0-31).
Custom drawable widget with full control.
{
type: 'custom',
name: 'customWidget',
x: 10,
y: 20,
width: 200,
height: 100,
onDraw: function(g) {
g.colour = 1;
g.rect(0, 0, this.width, this.height);
g.text('Custom Content', 10, 10);
}
}
The graphics context provides methods for drawing shapes and text.
Dropdown selection list.
{
type: 'dropdown',
name: 'modeDropdown',
x: 10,
y: 20,
width: 150,
height: 24,
items: ['Normal', 'Testing', 'Closed'],
selectedIndex: 0,
onChange: (index) => {
console.log(`Selected: ${index}`);
}
}
The list of dropdown items.
The index of the selected item.
The currently displayed text.
Visual grouping box with border and label.
{
type: 'groupbox',
x: 10,
y: 20,
width: 200,
height: 100,
text: 'Options'
}
Show Group Box Properties
Static text label.
{
type: 'label',
x: 10,
y: 20,
width: 200,
height: 14,
text: 'Park Rating: 999',
textAlign: 'left'
}
Text alignment: “left” or “centred”.
Scrollable list with columns and rows.
{
type: 'listview',
name: 'guestList',
x: 10,
y: 20,
width: 380,
height: 200,
scrollbars: 'vertical',
isStriped: true,
showColumnHeaders: true,
columns: [
{ header: 'Name', width: 150 },
{ header: 'Happiness', width: 80 },
{ header: 'Cash', width: 150 }
],
items: [
['John Smith', '255', '£50.00'],
['Jane Doe', '200', '£100.00']
],
canSelect: true,
onClick: (item, column) => {
console.log(`Clicked row ${item}, column ${column}`);
}
}
Show List View Properties
Scrollbar type: “none”, “horizontal”, “vertical”, “both”.
Whether to show alternating row colours.
Whether to show column headers.
Row items (arrays of strings or separator objects).
The currently selected cell.
The currently highlighted cell.
Whether rows can be selected.
Numeric spinner with increment/decrement buttons.
{
type: 'spinner',
name: 'valueSpinner',
x: 10,
y: 20,
width: 150,
height: 24,
text: 'Value: 100',
onIncrement: () => {
// Increase value
},
onDecrement: () => {
// Decrease value
}
}
Text Box Widget
Text input field.
{
type: 'textbox',
name: 'nameInput',
x: 10,
y: 20,
width: 200,
height: 24,
text: '',
maxLength: 32,
onChange: (text) => {
console.log(`Text changed: ${text}`);
}
}
focus()
Sets keyboard focus to the text box.
const textbox = window.findWidget('nameInput');
textbox.focus();
Embedded viewport showing the game world.
{
type: 'viewport',
name: 'minimap',
x: 10,
y: 20,
width: 200,
height: 150
}
Usage Examples
Interactive Settings Panel
const settings = {
pauseOnOpen: false,
autoSave: true,
volume: 50
};
ui.openWindow({
classification: 'settings',
title: 'Settings',
width: 300,
height: 200,
widgets: [
{
type: 'checkbox',
name: 'pauseCheckbox',
x: 10,
y: 20,
width: 280,
height: 14,
text: 'Pause on window open',
isChecked: settings.pauseOnOpen,
onChange: (checked) => {
settings.pauseOnOpen = checked;
}
},
{
type: 'checkbox',
x: 10,
y: 40,
width: 280,
height: 14,
text: 'Auto-save enabled',
isChecked: settings.autoSave,
onChange: (checked) => {
settings.autoSave = checked;
}
},
{
type: 'spinner',
x: 10,
y: 70,
width: 150,
height: 24,
text: `Volume: ${settings.volume}`,
onIncrement: function() {
settings.volume = Math.min(100, settings.volume + 10);
this.text = `Volume: ${settings.volume}`;
},
onDecrement: function() {
settings.volume = Math.max(0, settings.volume - 10);
this.text = `Volume: ${settings.volume}`;
}
},
{
type: 'button',
x: 100,
y: 120,
width: 100,
height: 24,
text: 'Apply',
onClick: () => {
console.log('Settings applied:', settings);
ui.closeWindows('settings');
}
}
]
});
Guest List Window
function openGuestList() {
const guests = map.getAllEntities('guest');
const items = guests.map(g => [
g.name,
g.happiness.toString(),
`£${(g.cash / 10).toFixed(2)}`
]);
ui.openWindow({
classification: 'guest-list',
title: 'Guest List',
width: 400,
height: 400,
widgets: [
{
type: 'listview',
x: 5,
y: 20,
width: 390,
height: 340,
scrollbars: 'vertical',
isStriped: true,
showColumnHeaders: true,
columns: [
{ header: 'Name', width: 150 },
{ header: 'Happiness', width: 100 },
{ header: 'Cash', width: 140 }
],
items: items,
canSelect: true,
onClick: (row, col) => {
const guest = guests[row];
ui.mainViewport.scrollTo({
x: guest.x,
y: guest.y,
z: guest.z
});
}
},
{
type: 'button',
x: 150,
y: 370,
width: 100,
height: 24,
text: 'Refresh',
onClick: () => {
ui.closeWindows('guest-list');
openGuestList();
}
}
]
});
}
ui.openWindow({
classification: 'graph',
title: 'Park Rating History',
width: 400,
height: 300,
widgets: [
{
type: 'custom',
x: 10,
y: 20,
width: 380,
height: 260,
onDraw: function(g) {
// Draw graph background
g.colour = 12;
g.box(0, 0, this.width, this.height);
// Draw grid lines
g.colour = 10;
for (let i = 0; i < 10; i++) {
const y = i * (this.height / 10);
g.line(0, y, this.width, y);
}
// Draw rating data (example)
g.colour = 2;
const data = [700, 750, 800, 780, 820, 850, 870, 900, 920, 950];
for (let i = 1; i < data.length; i++) {
const x1 = (i - 1) * (this.width / (data.length - 1));
const y1 = this.height - (data[i - 1] / 1000 * this.height);
const x2 = i * (this.width / (data.length - 1));
const y2 = this.height - (data[i] / 1000 * this.height);
g.line(x1, y1, x2, y2);
}
// Draw labels
g.colour = 0;
g.text('Rating', 5, 5);
}
}
]
});