Skip to main content
Widgets are UI elements that can be placed in custom windows. OpenRCT2 provides a variety of widget types for building interactive interfaces.

Widget Types

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

Base Widget Properties

All widgets share these common properties:
type
WidgetType
The widget type.
window
Window
The window containing this widget.
x
number
The x position relative to the window.
y
number
The y position relative to the window.
width
number
The width in pixels.
height
number
The height in pixels.
name
string
The widget name (used with findWidget).
tooltip
string
The tooltip text shown on hover.
isDisabled
boolean
Whether the widget is disabled.
isVisible
boolean
Whether the widget is visible.

Button Widget

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!');
  }
}
{
  type: 'button',
  x: 10,
  y: 10,
  width: 100,
  height: 24,
  text: 'Save',
  onClick: () => {
    // Save action
  }
}

Checkbox Widget

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

Colour Picker Widget

Colour selection widget.
{
  type: 'colourpicker',
  name: 'colourPicker',
  x: 10,
  y: 20,
  width: 24,
  height: 24,
  colour: 0,
  onChange: (colour) => {
    console.log(`Selected colour: ${colour}`);
  }
}

Custom Widget

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

Group Box Widget

Visual grouping box with border and label.
{
  type: 'groupbox',
  x: 10,
  y: 20,
  width: 200,
  height: 100,
  text: 'Options'
}

Label Widget

Static text label.
{
  type: 'label',
  x: 10,
  y: 20,
  width: 200,
  height: 14,
  text: 'Park Rating: 999',
  textAlign: 'left'
}

List View Widget

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

Spinner Widget

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();

Viewport Widget

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();
        }
      }
    ]
  });
}

Custom Graph Widget

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