# API de scripts Lua do pxstud.io — Formulários de configuração

> Parte da referência de scripts Lua do pxstud.io, cuja visão geral (https://pxstud.io/pt-br/docs/scripting.md) explica as convenções. Os grupos de opções e os tipos a que ela remete estão em https://pxstud.io/pt-br/docs/scripting/option-groups.md e https://pxstud.io/pt-br/docs/scripting/types.md. Tudo em um único arquivo (em inglês): https://pxstud.io/llms-full.txt. O índice de todo o site (em inglês): https://pxstud.io/llms.txt.

## Formulários de configuração
Um script pode declarar um pequeno formulário que é exibido antes de ele rodar. O que o usuário preenche chega como a tabela app.config, indexada pelo id de cada campo. Os campos são dados simples (um script nunca descreve a interface) e o editor os renderiza e valida. Cores, pincéis e paletas são o jeito de um script pedir algo do material do próprio usuário: o engine nunca lê as bibliotecas dele, então o script recebe exatamente o que o usuário escolher.

- `number` — Um campo numérico. Em Lua: number. Opções: `default` (O valor inicial.); `min` (O menor valor permitido.); `max` (O maior valor permitido.); `optional` (Permite deixá-lo vazio.); `randomMin / randomMax` (O intervalo de onde Aleatorizar sorteia, dentro de min..max (padrão min..max; sem os dois limites, Aleatorizar o deixa como está). Números inteiros quando os limites e o padrão forem inteiros.); `randomize` (false mantém o valor quando o usuário aleatoriza o formulário.).
- `text` — Uma linha de texto. Em Lua: string. Opções: `default` (O texto inicial.); `optional` (Permite deixá-lo vazio.).
- `checkbox` — Um interruptor de ligado/desligado. Em Lua: boolean. Opções: `default` (Se ele começa marcado.); `randomize` (false mantém o valor quando o usuário aleatoriza o formulário.).
- `select` — Uma escolha em uma lista fixa. Em Lua: string (o valor da opção escolhida). Opções: `options` (As opções, cada uma como { value, label }.); `default` (O valor selecionado no início.); `randomize` (false mantém a escolha quando o usuário aleatoriza o formulário.).
- `color` — Uma cor, escolhida com o seletor de cores do editor, com a paleta do sprite e as cores recentes a um clique. Dê a cada função o seu próprio campo (folha clara, folha escura, casca...) e o usuário escolhe todas. Em Lua: string, "#rrggbb" (ou "#rrggbbaa" com alfa), aceita diretamente em qualquer lugar onde uma cor é esperada. Opções: `default` (A cor inicial, "#rrggbb" ou "#rrggbbaa" (preto se omitida).); `randomize` (true deixa Aleatorizar sortear uma cor também (mantendo o alfa). Desativado por padrão: as cores da maioria dos scripts são ajustadas à mão.).
- `brush` — Um dos pincéis salvos do usuário, escolhido na biblioteca de pincéis dele. Em Lua: tabela { name, width, height, mask }: passe-a para app.defineBrush; ausente quando opcional e deixado vazio. Opções: `optional` (Permite deixá-lo vazio (então app.config.<id> é nil).).
- `palette` — Uma das paletas salvas do usuário, escolhida na biblioteca de paletas dele. Em Lua: lista de strings "#rrggbb" (ou "#rrggbbaa"): passe-a para app.setPalette ou escolha nela com cálculo de cores; ausente quando opcional e deixada vazia. Opções: `optional` (Permite deixá-la vazia (então app.config.<id> é nil).).

Exemplo de formulário (JSON) e do script que o usa:

```json
{
  "fields": [
    {
      "id": "leafLight",
      "type": "color",
      "label": "Leaf (light)",
      "default": "#6cc24a"
    },
    {
      "id": "leafMid",
      "type": "color",
      "label": "Leaf (mid)",
      "default": "#3a8f4a"
    },
    {
      "id": "leafDark",
      "type": "color",
      "label": "Leaf (dark)",
      "default": "#23603a"
    },
    {
      "id": "barkLight",
      "type": "color",
      "label": "Bark (light)",
      "default": "#8a5a35"
    },
    {
      "id": "barkDark",
      "type": "color",
      "label": "Bark (dark)",
      "default": "#5a3a22"
    },
    {
      "id": "stamp",
      "type": "brush",
      "label": "Leaf brush",
      "optional": true
    }
  ]
}
```

```lua
local layer, frame = app.getActiveLayerId(), app.getActiveFrameId()
local c = app.config

-- trunk: two bark colors the user chose
app.drawRectangle(layer, frame, 14, 20, 17, 31, { color = c.barkDark, fill = "filled" })
app.drawRectangle(layer, frame, 14, 20, 15, 31, { color = c.barkLight, fill = "filled" })

-- canopy: three leaf colors, with the user's own brush if they picked one
if c.stamp then app.defineBrush("leaf", c.stamp) end
for i, color in ipairs({ c.leafDark, c.leafMid, c.leafLight }) do
  app.drawStroke(layer, frame, { { 8, 18 - i * 4 }, { 24, 18 - i * 4 } },
    { color = color, brush = c.stamp and "leaf" or nil, brushSize = 4 })
end
```

### Ramificação
Qualquer campo pode ter um when, { "<id de um campo anterior>": [valores] }, e então só aparece enquanto cada select citado tiver um desses valores de opção (ou o checkbox for true/false) e ele próprio estiver visível. As condições só olham para selects e checkboxes anteriores, então encadeiam em qualquer profundidade: mude a raiz e o ramo inteiro some. Um campo oculto fica de fora de app.config, então o script recorre ao próprio padrão: leia todos os campos como opcionais.

```json
{
  "fields": [
    {
      "id": "kind",
      "type": "select",
      "label": "Kind",
      "options": [
        {
          "value": "broadleaf",
          "label": "Broadleaf"
        },
        {
          "value": "conifer",
          "label": "Conifer"
        }
      ],
      "default": "broadleaf"
    },
    {
      "id": "crown",
      "type": "select",
      "label": "Crown",
      "options": [
        {
          "value": "round",
          "label": "Round"
        },
        {
          "value": "weeping",
          "label": "Weeping"
        }
      ],
      "when": {
        "kind": [
          "broadleaf"
        ]
      }
    },
    {
      "id": "droop",
      "type": "number",
      "label": "Droop",
      "min": 0,
      "max": 100,
      "randomMin": 20,
      "randomMax": 80,
      "when": {
        "crown": [
          "weeping"
        ]
      }
    },
    {
      "id": "tiers",
      "type": "number",
      "label": "Tiers",
      "min": 2,
      "max": 12,
      "when": {
        "kind": [
          "conifer"
        ]
      }
    },
    {
      "id": "newLayer",
      "type": "checkbox",
      "label": "Draw on a new layer",
      "randomize": false
    }
  ]
}
```

### Aleatorizar
Todo formulário recebe um botão Aleatorizar. Ele sorteia cada número dentro de randomMin..randomMax (padrão min..max; um número sem os dois limites é mantido), escolhe qualquer opção de select e inverte os checkboxes; as cores só são sorteadas com randomize: true, e textos, pincéis e paletas nunca são tocados. Defina randomMin/randomMax com o intervalo que ainda fica bom e randomize: false nas configurações que não têm a ver com a arte (nova camada, respeitar a seleção...).
