Table of Contents

Add controls and tables to a widget

Use standard components for forms, choices, status lists, and short explanations. They follow the application theme and remain legible when the player changes fonts or colors.

A script-created settings panel with a checkbox, radio choices, tooltip, flexible spacing, and a small table

Keep controls and state in agreement

Checkbox and Radio are controlled components. They display the value supplied through checked or selected. Their callbacks report a choice but do not retain it for you. Update the state that supplies the displayed value:

import { createState } from "smudgy:core";
import {
  Checkbox,
  Column,
  Radio,
  Row,
  Space,
  Table,
  Text,
  Tooltip,
  createWidget,
} from "smudgy:widgets";
 
const prefs = createState<{ autoloot: boolean; mode: string }>("manual-preferences");
prefs.set({ autoloot: false, mode: "careful" });
 
const selectMode = (mode: string) => {
  prefs.value.mode = mode;
};
 
createWidget("preferences", (
  <Column spacing={8} padding={10}>
    <Row spacing={8}>
      <Checkbox
        checked={prefs.bind("autoloot")}
        onToggle={(checked) => { prefs.value.autoloot = checked; }}
      >
        Autoloot
      </Checkbox>
      <Space width="fill" />
      <Tooltip tip="Changes how quickly the package sends movement commands.">
        <Text>Movement mode</Text>
      </Tooltip>
    </Row>
 
    <Row spacing={8}>
      <Radio value="fast" selected={prefs.bind("mode")} onSelect={selectMode}>Fast</Radio>
      <Radio value="careful" selected={prefs.bind("mode")} onSelect={selectMode}>Careful</Radio>
    </Row>
 
    <Table
      columns={[
        { header: "Setting", width: "fill" },
        { header: "Current value", align_x: "right" },
      ]}
      rows={[
        ["Autoloot", prefs.bind("autoloot")],
        ["Movement", prefs.bind("mode")],
      ]}
      width="fill"
      padding={4}
      separator={1}
    />
  </Column>
));

Without an onToggle callback, a checkbox is disabled and can serve as a read-only indicator. A radio requires onSelect because a clickable choice that cannot report its value would be misleading.

Form a radio group

There is no separate radio-group component. Radios form a group by reading and updating the same selected state. They may appear in different rows or columns and still represent one choice.

Radio.value accepts a string or number. The callback receives the selected value as a string, so convert it when the stored value must remain numeric.

Use tooltips sparingly

A tooltip should clarify an unfamiliar control, abbreviation, or value. Do not place instructions required to complete a task only in a tooltip; touch and keyboard users may not encounter it.

Text tips receive the standard tooltip appearance. An element tip uses the layout and styles declared by that element. Set tip to false or null to suppress it conditionally.

Update a table

A table's columns and rows describe its structure when the widget is mounted. Bind individual cells when only their displayed values change. Re-mount the widget when rows are added, removed, or reordered.

A row with fewer cells than columns is padded with empty cells. A row with more cells than columns is invalid. Wrap a table in Scrollable when its height can exceed the pane.

Reference: Controls, Text and data display, and Layout and overlays.